R Notebook

#Introduction We chose to focus on rich people, or people presumably with high-income jobs, throughout our project. People are generally really interested in the lives of rich people, and this is really appealing to readers, which makes it newsworthy. We noticed this specifically this week in the Wall Street Journal where many of the front page articles pertained to the inside lives of wealthier people, for example investment bankers and more.

For example, in our question about what people are contributing to with their specific jobs, we focused on CEO’s who presumably make a good living. We then looked at that across Democratic and Republican parties to note the differences between these two groups in terms of the same occupation.

#Process and Question Development Do more Marylanders donate to in-state or out-of-state candidates? Which out-of-state candidates received the most donations and the greatest amount of money in donations? What are the demographics of people who donate to Maryland races only? To out-of-state races only? (using voter data OR using census data to see if those donors are clustered in specific locations, and, if they are, what the makeups of those locations are) Among top Maryland donors, what professions donate the most money to senate campaigns? What’s the makeup of donations received by Hogan and Alsobrooks? What percentage of their overall donations were large amounts of money (to be defined, but > $1000, for example) vs small amounts? Which party tends to donate more to out-of-state races?

We refined our dataset further by deciding to focus on individual donations made to senate races by donors in Maryland. Using the FEC website, we downloaded a dataset that met those parameters. We cleaned the data by removing unnecessary columns and renaming the columns where needed. We decided to limit our dataset to donations made in 2024 to ensure that the donations were made after the primary and before the general election. We hoped this would limit the number of candidates receiving donations, since most donations will go toward one of two parties in a certain race.

We also cleaned the data-set, removing numerous columns that were N/A or that were unnecessary to our project. We also had to rename one column and then filter to only the report year of 2024 to limit the donations. To answer our first question and questions, we’ll need to separate the out-of-state candidates from in-state candidates, but this should be easy because we’ll just need to filter out the Hogan and Alsobrooks committees to isolate the out-of-state candidates receiving donations. To answer our third question, we’ll need to either call in demographic data from the census or identify specific locations with clusters of donations, and look up information about those locations using census.gov. Question #4 asks about the professions of donors. One obvious challenge here is that professions and titles are often spelled or worded differently, even if they describe the same job– in this dataset, there are 2,738 different professions listed. To try to combat this, we will use open refine to reduce the number of occupations with slight variations in spelling or wording. We will then limit the data set to the top 1000 donations to make the analysis more manageable. We can also make some broad observations about the data even without cleaning the professions– for example, it’s obvious that the most donations come from people who say they are “not employed” (45568) or “retired” (29424). The next highest number of donations is more than 20,000 donations less than the number of donations from retired people. There are 207 committees that received donations in our dataset. Originally, we thought that we would use open refine to sort the committees by candidate name, because we thought that most candidates would have multiple committees. However, we found that this was actually pretty uncommon, and we weren’t able to match any names using open refine, once we uploaded our csv. For question 5, we need to do some extra research to decide how to define large donations. One idea is to find the average or median donation, and use that number as the dividing line between small and large donations. Another idea is to see if there’s an agreed-upon definition by people who work on campaigns or political scientists. We have one extra question (#6) that we would like to answer, but it involves party-level analysis that we can’t do because we don’t have the political parties of the donors in this data, and we don’t have anything in this data that indicates the party of each candidate. To answer this question, we could join this data with data from Act Blue and Win Red to identify the parties of donors. We can also probably find a dataset with the committee names and parties for all senate candidates, and join that dataset with ours.

#Refining the dataset

options(scipen=999)
library(tidyverse)
library(lubridate)
library(dplyr)
install.packages('tidycensus')

  There is a binary version available but the source version is later:
installing the source package ‘tidycensus’

trying URL 'https://cran.rstudio.com/src/contrib/tidycensus_1.6.7.tar.gz'
Content type 'application/x-gzip' length 2262396 bytes (2.2 MB)
==================================================
downloaded 2.2 MB

* installing *source* package ‘tidycensus’ ...
** package ‘tidycensus’ successfully unpacked and MD5 sums checked
** using staged installation
** R
** data
*** moving datasets to lazyload DB

* removing ‘/Library/Frameworks/R.framework/Versions/4.2/Resources/library/tidycensus’
* restoring previous ‘/Library/Frameworks/R.framework/Versions/4.2/Resources/library/tidycensus’
Warning in install.packages :
  installation of package ‘tidycensus’ had non-zero exit status

The downloaded source packages are in
    ‘/private/var/folders/2l/xcvc2w597t9714c985cwdbr40000gp/T/RtmpfQuLsL/downloaded_packages’
library(tidycensus)
md_senate_contributions <- read_csv("data/md_senate_contributions.csv") |>


print(column_names)
New names:Warning: One or more parsing issues, call `problems()` on your data frame for details, e.g.:
  dat <- vroom(...)
  problems(dat)Rows: 115275 Columns: 79── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (44): committee_id, committee_name...2, report_type, line_number, transaction_id, entity_type, entity_type_desc, unused_contbr_id, ...
dbl  (11): report_year, image_number, file_number, contributor_zip, contribution_receipt_amount, contributor_aggregate_ytd, conduit_comm...
lgl  (22): committee_name...9, recipient_committee_org_type, memo_code_full, candidate_id, candidate_name, candidate_first_name, candida...
dttm  (2): contribution_receipt_date, load_date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

clean data:

cleaned_data <- md_senate_contributions |>

select( -unused_contbr_id, -committee_name...9, -recipient_committee_org_type, -contributor_suffix, -contributor_street_2, -contributor_id, -memo_code, -memo_code_full, -candidate_id, -candidate_name, -candidate_first_name, -candidate_last_name, -candidate_middle_name, -candidate_prefix, -candidate_suffix, -candidate_office, -candidate_office_full, -candidate_office_state, -candidate_office_state_full, -candidate_office_district, -conduit_committee_id, -conduit_committee_name, -conduit_committee_street1, -conduit_committee_street2, -conduit_committee_city, -conduit_committee_state, -conduit_committee_zip, -donor_committee_name, -national_committee_nonfederal_account, -election_type_full, -increased_limit, -is_individual) |>

rename(committee_name = `committee_name...2`) |>

filter(report_year == 2024)


colnames(cleaned_data)
 [1] "committee_id"                    "committee_name"                  "report_year"                     "report_type"                    
 [5] "image_number"                    "line_number"                     "transaction_id"                  "file_number"                    
 [9] "entity_type"                     "entity_type_desc"                "contributor_prefix"              "contributor_name"               
[13] "recipient_committee_type"        "recipient_committee_designation" "contributor_first_name"          "contributor_middle_name"        
[17] "contributor_last_name"           "contributor_street_1"            "contributor_city"                "contributor_state"              
[21] "contributor_zip"                 "contributor_employer"            "contributor_occupation"          "receipt_type"                   
[25] "receipt_type_desc"               "receipt_type_full"               "contribution_receipt_date"       "contribution_receipt_amount"    
[29] "contributor_aggregate_ytd"       "election_type"                   "fec_election_type_desc"          "fec_election_year"              
[33] "amendment_indicator"             "amendment_indicator_desc"        "schedule_type_full"              "load_date"                      
[37] "original_sub_id"                 "back_reference_transaction_id"   "back_reference_schedule_name"    "filing_form"                    
[41] "link_id"                         "memo_text"                       "two_year_transaction_period"     "schedule_type"                  
[45] "sub_id"                          "pdf_url"                         "line_number_label"              
most_contributions <- cleaned_data |>
select(committee_name, contribution_receipt_amount) |>
group_by(committee_name) |>
summarize(total_contribution = sum(contribution_receipt_amount, na.rm = TRUE)) |>
arrange(desc(total_contribution)) 
cleaned_data |>
  
write_csv("data/cleaned_md_senate_contributions.csv")
data <- read_delim("data/ccl.txt", delim = "|", col_names = FALSE)
Rows: 8568 Columns: 7── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "|"
chr (4): X1, X4, X5, X6
dbl (3): X2, X3, X7
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
cleaned_data_ccl <- data |>
rename(candidate_id = X1,candidate_election_year = X2, fec_election_year = X3, committee_id = X4,committee_type = X5,committee_design = X6, linkage_id = X7)

inner join the committee id and fec election year:

joined_data <- inner_join(cleaned_data, cleaned_data_ccl, by = c("committee_id", "fec_election_year"))
Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

insert the candidate data:

candidate_data <- read_delim("data/candidate_data.txt", delim = "|", col_names = FALSE) |>
  select(X1, X2, X3, X5) |> 
 rename(candidate_id = X1, candidate_name = X2, candidate_party = X5)
Rows: 3824 Columns: 30── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "|"
chr  (7): X1, X2, X3, X5, X19, X20, X28
dbl (18): X4, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X26, X27, X29, X30
lgl  (5): X21, X22, X23, X24, X25
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
candidate_joined_data <- inner_join(joined_data, candidate_data, by =c("candidate_id"))

include only these rows:


final_data <- candidate_joined_data |>
select(committee_id, committee_name, report_year, entity_type_desc,contributor_prefix, contributor_name, contributor_first_name, contributor_last_name, contributor_middle_name, contributor_street_1,contributor_city, contributor_state, contributor_zip,contributor_employer, contributor_occupation, contribution_receipt_date, contribution_receipt_amount, contributor_aggregate_ytd,memo_text,pdf_url, candidate_id, candidate_election_year)

#Question 1: Do more Marylanders donate to in-state or out-of-state candidates?

final_data_states <- final_data |>
  mutate(state = substr(candidate_id, 3, 4))
state_contributions <- final_data_states |>
  group_by(state) |>
  summarise(
    contribution_count = n(),                
    total_contribution = sum(contribution_receipt_amount, na.rm = TRUE)  
  )
maryland_contrib <- state_contributions |>
  filter(state == "MD") |>
  summarise(total_maryland_contrib = sum(total_contribution, na.rm = TRUE))


other_states_contrib <- state_contributions |>
  filter(state != "MD") |>
  summarise(total_other_states_contrib = sum(total_contribution, na.rm = TRUE))


comparison <- data.frame(
  state = c("Maryland", "Other States"),
  total_contribution = c(maryland_contrib$total_maryland_contrib, other_states_contrib$total_other_states_contrib)
)

#Answer 1: Montana was surprisingly the state with the most contributions — edging Maryland with 202 more contributions Maryland. Maryland received the highest total sum however, with $5,516,562.24. That was $4,074,226.07 more than the next highest state, which was Montana. The other states combined recieved $7,358,970, a little less than two million more than just the state of Maryland.

#Question 2: Which out-of-state candidates received the most donations and the greatest amount of money in donations?

final_data_no_md <- final_data_states |>
  filter(state != "MD")
contributions_summary <- final_data_no_md |>
  group_by(committee_name) |>
  summarise(
    num_contributions = n(), 
    total_contribution_sum = sum(contribution_receipt_amount, na.rm = TRUE) 
  ) |>
  arrange(committee_name)

#Answer 2: Jon Tester, Sherrod Brown and Ruben Gallego were the top three in that order for both contributions recieved and the sum of contributions. The similarity between the three of them is that they were all Democrats running in tightly contested races. Brown was the only to lose his race. Jon Tester also clearly recieved more than the other two — which explains why Montana received so many more out-of-state contributions than other states. Montanans for Tester received 3,679 more contributions and $238,017.96 more in total contributions compared to Friends of Sherrod Brown.

We chose not to focus on refining and coding this question. Looking at donations to candidates like Tester and Alsobrooks would not answer our newsworthiness because our focus is on where wealthy individuals’ money goes in elections. While it is interesting that out-of-state donations align with competitive Senate races, such as Montana and Ohio, this analysis would primarily highlight geographic donation trends rather than the behavior of high-income donors. Our story aims to display how wealthy individuals, particularly CEOs, use their financial power in election donations. Examining specific candidates or out-of-state donations would not provide the same insight into the role of wealth and leadership positions in shaping overall donation patterns, which makes our findings newsworthy.

However, the other questions we explored provided were more relevant. By focusing on CEOs, we were able to show patterns of financial influence resonating with public interest. People are often more curious about how wealthy individuals spend their money, especially in the context of elections, and we felt that the other questions we explored provided more relevant insights that aligned with our potential news story about where wealthy individuals’ money goes in elections.

#Question 3: What are the demographics of people who donate to Maryland races only? To out-of-state races only?

Step 1: find people who donate to ONLY Maryland races.


maryland_only_donors <- final_data_states|>
  group_by(contributor_name) |>                
  summarize(only_md = all(state == "MD")) |>
  filter(only_md) |>                          
  select(contributor_name)                    

full_maryland_only_donors <- final_data_states |>
   filter(contributor_name %in% maryland_only_donors$contributor_name)


zips_full_maryland_only_donors <- full_maryland_only_donors |>
  group_by(contributor_name, contributor_zip) |>
  summarize(
    num_donations = n(),             
    total_donated = sum(contribution_receipt_amount) 
  )|>
  arrange(desc(num_donations))  
`summarise()` has grouped output by 'contributor_name'. You can override using the `.groups` argument.
md_zip_summary <- zips_full_maryland_only_donors |>
  group_by(contributor_zip) |>              
  summarize(
    num_donations = n(),                      
    total_donated = sum(total_donated)      
  ) |>
  arrange(desc(num_donations)) 
all_zcta <- get_acs(
  geography = "zcta",
  variables = c(
 "B01003_001", 
     "B01002_001", #= "median_age",
 "B19013_001", # = "median_income",
  "B02001_002", # = "white_alone",
     "B02001_003", # = "black_alone",
"B02001_004", # = "native_american",
    "B02001_005", #= "asian_alone",
 "B02001_006", # = "hawaiian_pacific",
"B02001_007",  # = "other_race",
    "B02001_008", # = "two_or_more_races"
"B03002_012E"),
  
  year = 2022,
  survey = "acs5",
  output = "wide"
)
Getting data from the 2018-2022 5-year ACS
all_zcta <- all_zcta %>%
  rename(
    total_population = B01003_001E,      
    median_age = B01002_001E,            
    median_income = B19013_001E,        
    white_alone = B02001_002E,          
    black_alone = B02001_003E,         
    native_american = B02001_004E,      
    asian_alone = B02001_005E,          
    hawaiian_pacific = B02001_006E,     
    other_race = B02001_007E,           
    two_or_more_races = B02001_008E,
    hispanic_or_latino = B03002_012E
  )

zips_full_maryland_only_donors <- zips_full_maryland_only_donors |>
  mutate(
    contributor_zip = substr(contributor_zip, 1, 5),
    contributor_zip = sprintf("%05s", contributor_zip)
  )

zips_full_maryland_only_donors <- zips_full_maryland_only_donors |>
  mutate(contributor_zip = as.character(contributor_zip))

merged_data_md <- zips_full_maryland_only_donors %>%
  left_join(all_zcta, by = c("contributor_zip" = "GEOID"))

summarized_md_donor_demos_by_zip <- merged_data_md %>%
  group_by(contributor_zip) |>
  summarize(
    total_population = first(total_population),  
    median_age = first(median_age),             
    median_income = first(median_income),       
    white_alone = first(white_alone),            
    black_alone = first(black_alone),           
    native_american = first(native_american),   
    asian_alone = first(asian_alone),           
    hawaiian_pacific = first(hawaiian_pacific),  
    other_race = first(other_race),  
     hispanic_or_latino = first(hispanic_or_latino),
    two_or_more_races = first(two_or_more_races),
    num_donations = n(),                      
    total_amt_donated = sum(total_donated, na.rm = TRUE) 
  ) |> 
  arrange(desc(num_donations))  |> 
  mutate(
    white_alone_per_capita = white_alone / total_population,
    black_alone_per_capita = black_alone / total_population,
    native_american_per_capita = native_american / total_population,
    asian_alone_per_capita = asian_alone / total_population,
    hawaiian_pacific_per_capita = hawaiian_pacific / total_population,
    other_race_per_capita = other_race / total_population,
     hispanic_or_latino_per_capita = hispanic_or_latino / total_population,
    two_or_more_races_per_capita = two_or_more_races / total_population,
    median_income_per_capita = median_income / total_population
  ) |>
   mutate(
    white_alone_percent = white_alone_per_capita * 100,
    black_alone_percent = black_alone_per_capita * 100,
    native_american_percent = native_american_per_capita * 100,
    asian_alone_percent = asian_alone_per_capita * 100,
    hawaiian_pacific_percent = hawaiian_pacific_per_capita * 100,
    other_race_percent = other_race_per_capita * 100,
    hispanic_or_latino_percent = hispanic_or_latino_per_capita *100,
    two_or_more_races_percent = two_or_more_races_per_capita * 100,
    median_income_per_capita = median_income_per_capita * 100
  ) |>
 mutate(
    amt_donated_per_resident = total_amt_donated / total_population
  ) |>   
  select(where(~ !all(is.na(.))))

cleaner_md_donor_demos <- summarized_md_donor_demos_by_zip |> 
    select(-matches("_per_capita"), -"native_american", -"hawaiian_pacific", -"other_race", -"two_or_more_races", -"white_alone", -"black_alone", -"asian_alone") 
 

repeat for out of state donors:

out_of_state_donors <- final_data_states |>
  filter(state != "MD")

xmaryland_donors <- final_data_states |>
  filter(state == "MD")

out_of_state_only_donors <- out_of_state_donors |>
  filter(!contributor_name %in% xmaryland_donors$contributor_name)  |>
group_by(contributor_name, contributor_zip) |>
  summarize(
    num_donations = n(),            
    total_donated = sum(contribution_receipt_amount)  
  ) |>
  arrange(desc(num_donations))  
`summarise()` has grouped output by 'contributor_name'. You can override using the `.groups` argument.
out_of_state_only_donors <- out_of_state_only_donors |>
  mutate(
    contributor_zip = substr(contributor_zip, 1, 5),
    contributor_zip = sprintf("%05s", contributor_zip)
  )

merged_data_oos <- out_of_state_only_donors|>
  left_join(all_zcta, by = c("contributor_zip" = "GEOID"))

cleaner_oos_donor_demos <- merged_data_oos  |>
   group_by(contributor_zip) |>
  summarize(
    total_population = first(total_population), 
    median_age = first(median_age),              
    median_income = first(median_income),        
    white_alone = first(white_alone),         
    black_alone = first(black_alone),            
    native_american = first(native_american),   
    asian_alone = first(asian_alone),           
    hawaiian_pacific = first(hawaiian_pacific),
    other_race = first(other_race),     
    hispanic_or_latino = first(hispanic_or_latino),
    two_or_more_races = first(two_or_more_races),
    num_donations = n(),                      
    total_amt_donated = sum(total_donated, na.rm = TRUE) 
  ) |> 
  arrange(desc(num_donations))  |> 
  mutate(
   
    white_alone_per_capita = white_alone / total_population,
    black_alone_per_capita = black_alone / total_population,
    native_american_per_capita = native_american / total_population,
    asian_alone_per_capita = asian_alone / total_population,
    hawaiian_pacific_per_capita = hawaiian_pacific / total_population,
    other_race_per_capita = other_race / total_population,
    hispanic_or_latino_per_capita = hispanic_or_latino / total_population,
    two_or_more_races_per_capita = two_or_more_races / total_population,
    median_income_per_capita = median_income / total_population
  ) |>
   mutate(
  
    white_alone_percent = white_alone_per_capita * 100,
    black_alone_percent = black_alone_per_capita * 100,
    native_american_percent = native_american_per_capita * 100,
    asian_alone_percent = asian_alone_per_capita * 100,
    hawaiian_pacific_percent = hawaiian_pacific_per_capita * 100,
    other_race_percent = other_race_per_capita * 100,
    hispanic_or_latino_percent = hispanic_or_latino_per_capita *100,
    two_or_more_races_percent = two_or_more_races_per_capita * 100,
    median_income_per_capita = median_income_per_capita * 100  
  ) 

cleaner_oos_donor_demos <- cleaner_oos_donor_demos |> select(-matches("_per_capita"), -"native_american", -"hawaiian_pacific", -"other_race", -"two_or_more_races", -"white_alone", -"black_alone", -"asian_alone") |> 
  
  mutate(
    amt_donated_per_resident = total_amt_donated / total_population
  ) |>
  select(where(~ !all(is.na(.))))
head(merged_data_oos)
cleaner_oos_donor_demos <- cleaner_oos_donor_demos |>   
  mutate(
    contributor_zip = as.character(contributor_zip),         # Ensure ZIP codes are strings
    contributor_zip = str_pad(contributor_zip, width = 5,    # Pad with leading zeros to ensure 5 digits
                              side = "left", pad = "0"),
    contributor_zip = ifelse(str_detect(contributor_zip, "^\\d{5}$"),  # Keep only valid 5-digit ZIP codes
                             contributor_zip, NA))  |>
mutate(contributor_zip = ifelse(contributor_zip == "30639", "20639", contributor_zip))



cleaner_oos_donor_demos |> arrange(desc(amt_donated_per_resident)) |> write_csv("cleaner_oos_donor_demos.csv")
cleaner_md_donor_demos <- cleaner_md_donor_demos |> 
  mutate(
    contributor_zip = as.character(contributor_zip),         # Ensure ZIP codes are strings
    contributor_zip = str_pad(contributor_zip, width = 5,    # Pad with leading zeros to ensure 5 digits
                              side = "left", pad = "0"),
    contributor_zip = ifelse(str_detect(contributor_zip, "^\\d{5}$"),  # Keep only valid 5-digit ZIP codes
                             contributor_zip, NA)) 

cleaner_md_donor_demos|>  arrange(desc(amt_donated_per_resident)) |> write_csv("cleaner_md_donor_demos.csv")
top_cleaner_md_donor_demos <- cleaner_md_donor_demos |>
 arrange(desc(amt_donated_per_resident)) |>
  filter(contributor_zip != "21287") |>
  slice_head(n = 5)

write_csv(top_cleaner_md_donor_demos, "top_cleaner_md_donor_demos.csv")
top_cleaner_oos_donor_demos <- cleaner_oos_donor_demos |>
    arrange(desc(amt_donated_per_resident)) |>
  slice_head(n = 5)

write_csv(top_cleaner_oos_donor_demos, "top_cleaner_oos_donor_demos.csv")

#Answer 3:

In asking and answering this question, we attempt to use donations as a measure of a donor’s perception of their donation’s ability to impact the outcome of a race, meaning that people who donated only to Maryland’s senate race, we infer, determined that their donation would be most impactful to that race. Conversely, people who donated only to out-of-state races determined that their donations were most needed to influence those races.

It is certainly possible that donors give money to candidates for other reasons (Maybe they’re just a big fan of a certain politican). However, given that the top out-of-state donations were made to candidates in competitive races, and that Maryland’s senate race was competitive this year, we think this is a reasonable theory that we can use to interpret this data.

To illustrate our findings, I first created two Datawrapper maps showing donation amount per capita and demographic data, including median income and age, and racial makeup.

In-state election donors: https://datawrapper.dwcdn.net/91uYI/1/ Out-of-state election donors: https://datawrapper.dwcdn.net/PllQC/1/

From comparing those maps, it’s clear that Marylanders in certain parts of the state (the wealthy DC suburbs, Annapolis, and parts of Talbot county) donate more money, whether they are donating to in-state or out-of-state elections.

We can see that for Maryland races, donors in those high-donation areas donated similar amounts per capita. However, for out-of-state races, the largest donations per capita were concentrated around the DC suburbs.

This is likely because Maryland’s wealthiest donors live in that part of the state, and made large donations to candidates Jon Tester, Sherrod Brown and Ruben Gallego in an effort to sway competitive races in swing states.

Donations to Maryland races were more evenly dispersed across the state. This may be because the Maryland race was competitive this year, and Marylanders felt that their contributions to the in-state senate race could make a difference in shaping the outcome of the election (as opposed to more typical election years, where Maryland is an uncontested blue state).

Question 4: Among top Maryland donors, what professions donate the most money to senate campaigns?

individual_donors  <- final_data_states|>
group_by(contributor_name, contributor_zip, contributor_occupation, contributor_employer) |> 
  summarize(
    num_donations = n(),               
    total_donated = sum(contribution_receipt_amount) 
  ) |>
  arrange(desc(num_donations))  

jobs_to_clean <- individual_donors |> 
  group_by(contributor_occupation) |>
  summarize(
    number_jobs = n(),
    num_donations = n(),  
     total_donations = sum(total_donated)
  ) |> arrange(desc(number_jobs)) |>
write_csv("data/jobs_to_clean.csv")
boss_to_clean  <- individual_donors |> 
  group_by(contributor_employer) |>
  summarize(
    number_jobs = n(),
    num_donations = n(),  
     total_donations = sum(total_donated)
  ) |> arrange(desc(number_jobs)) |>
write_csv("data/boss_to_clean.csv")
clean_employer <- read_csv("data/cleaned_boss.csv")
clean_occupation <- read_csv("data/clean_jobs.csv")

clean_occupation |> 
  group_by(cleaned_jjobs) |> 
  summarize(
    number_jobs = n(),
    num_donations = n(),  
    total_donated = sum(total_donations)) |> 
  arrange(cleaned_jjobs)


clean_occupation <- read_csv("data/clean_jobs.csv")


clean_occupation <- clean_occupation |> 
  mutate(cleaned_jjobs = case_when(
    contributor_occupation %in% c("C.E.O.", "CEO", "CEO & FOUNDER", "CEO & MEDIA CONTRIBUTOR", 
                                  "CEO & PRESIDENT", "CEO UNDERWRITING", "CEO/AUTHOR", 
                                  "CHAIRMAN, CEO AND PRESIDENT", "CHIEF EXECUTIVE OFFICER", 
                                  "CO-CEO", "CO-FOUNDER & CEO", "PRESIDENT & CEO", 
                                  "FOUNDER CEO", "PRESIDENT & C.E.O.", "PRESIDENT / CEO", 
                                  "PRESIDENT CEO") ~ "CEO",
    contributor_occupation %in% c("DEPUTY", "DEPUTY ADMINISTRATOR & DIRECTOR", 
                                  "DEPUTY ASSISTANT SECRETARY", "DEPUTY CHIEF ADMINISTRATIVE OFFICER", 
                                  "DEPUTY COS", "DEPUTY DIRECTOR", "DEPUTY RESEARCH DIRECTOR", 
                                  "DEPUTY SECRETARY", "DEPUTY SECRETARY OF COMMERCE") ~ "DEPUTY",
    TRUE ~ contributor_occupation  
  ))


overall_totals <- clean_occupation |> 
  summarize(
    total_jobs = sum(number_jobs, na.rm = TRUE),
    total_donations = sum(total_donations, na.rm = TRUE)
  )


ceo_deputy_proportions <- clean_occupation |> 
  filter(cleaned_jjobs %in% c("CEO", "DEPUTY")) |> 
  group_by(cleaned_jjobs) |> 
  summarize(
    total_number_jobs = sum(number_jobs, na.rm = TRUE),
    total_num_donations = sum(num_donations, na.rm = TRUE),
    total_donations = sum(total_donations, na.rm = TRUE)
  ) |> 
  mutate(
    prop_number_jobs = round((total_number_jobs / overall_totals$total_jobs) * 100, 2),
    prop_total_donations = round((total_donations / overall_totals$total_donations) * 100, 2)
  )


ceo_deputy_proportions

#Answer 4:

To answer this question, we put a csv of occupations into OpenRefine. We originally planned to limit the data set, but we found that there were only 1,963 job titles in the data set, which seemed like a reasonable number to refine down into a smaller list of jobs.

To do this, we grouped certain jobs into categories like “director” and “executive” – so job titles like “sales director” went into the “director” category.

We chose to focus on CEOS and began by pulling and merging all of the same terms for CEO. There were 20 of them. We then decided to compare that to deputies, which was extremely interesting to us because they are government employees. According to research, they are allowed to donate. We merged all of the names with deputy together. Then we created a new dataset with DEPUTY and CEO, but the numbers are disporportionate, so we made them proportinate as follows: prop_number_jobs Proportion of jobs in the dataset for each category, relative to the total number of jobs. prop_num_donations Proportion of the number of donations made, relative to the total donations count. prop_total_donations Proportion of the total dollar value donated, relative to the total donation amount.

Our analysis showed that CEOs make up only 1.88% of all jobs in the dataset, but they account for 3.59% of the total donations. This means that while there aren’t many CEOs compared to other job titles, they donate a lot more money, which reflects their financial influence.

On the other hand, DEPUTYs,many of whom are government employees, make up 0.08% of all jobs and contribute only 0.05% of total donations. This is a much smaller presence compared to CEOs and shows that public sector employees like DEPUTYs donate far less overall.

This is important because it highlights a clear difference between private-sector leadership roles and government positions when it comes to donations. CEOs, who often earn high salaries, seem to have much more financial power to make donations. DEPUTYs, in contrast, contribute far less, which might reflect differences in income or rules about political giving for public employees.

What stands out is that donations aren’t spread evenly across job types—most of the money comes from a small group of people in leadership positions. This raises interesting questions about income, influence, and how donations shape things like political campaigns or charitable efforts.

This is newsworthy because it highlights how a small group of high-income CEOs disproportionately drives donations, revealing economic disparities and the outsized financial influence of private-sector leaders.

#Question 5: What’s the makeup of donations received by Hogan and Alsobrooks? What percentage of their overall donations were large amounts of money (to be defined, but > $1000, for example) vs small amounts?

hogan_filtered_data <- final_data |>
  filter(committee_name %in% c("HOGAN FOR MARYLAND INC."))
large_donation_threshold <- 1000

hogan_filtered_data |> mutate( donation_category = ifelse(contribution_receipt_amount > large_donation_threshold, "Large", "Small") )

hogan_category_totals <- hogan_filtered_data |>
  mutate(donation_category = ifelse(contribution_receipt_amount > large_donation_threshold, "Large", "Small")) |>
  group_by(donation_category) |>
  summarise(total_amount = sum(contribution_receipt_amount, na.rm = TRUE), .groups = "drop")

hogan_category_totals %>% mutate( percentage = total_amount / sum(total_amount) * 100 )

hogan_category_totals
alsobrooks_filtered_data <- final_data |>
  filter(committee_name %in% c("ALSOBROOKS FOR SENATE"))

large_donation_threshold <- 1000

alsobrooks_category_totals <- alsobrooks_filtered_data |>
  mutate(donation_category = ifelse(contribution_receipt_amount > large_donation_threshold, "Large", "Small")) |>
  group_by(donation_category) |>
  summarise(total_amount = sum(contribution_receipt_amount, na.rm = TRUE), .groups = "drop")

alsobrooks_category_totals  %>% mutate( percentage = total_amount / sum(total_amount) * 100 )

alsobrooks_category_totals
hogan_filtered_data <- hogan_filtered_data |> 
  mutate(contributor_zip = substr(as.character(contributor_zip), 1, 5))
hogan_zips <- hogan_filtered_data |>
  group_by(contributor_zip) |>
  summarise(count = n())

write_csv(hogan_zips, "hogan_zips.csv")
alsobrooks_filtered_data <- alsobrooks_filtered_data |> 
  mutate(contributor_zip = substr(as.character(contributor_zip), 1, 5))
alsobrooks_zips <- alsobrooks_filtered_data |>
  group_by(contributor_zip) |>
  summarise(count = n())

write_csv(alsobrooks_zips, "alsobrooks_zips.csv")
hogan_large_data <- hogan_filtered_data |> 
  mutate(contributor_zip = substr(as.character(contributor_zip), 1, 5)) |> 
  filter(contribution_receipt_amount > 1000) |>
  group_by(contributor_zip) |>
  summarise(count = n())
write_csv(hogan_large_data, "hogan_large.csv")
alsobrooks_large_data <- alsobrooks_filtered_data |> 
  mutate(contributor_zip = substr(as.character(contributor_zip), 1, 5)) |> 
  filter(contribution_receipt_amount > 1000) |>
  group_by(contributor_zip) |>
  summarise(count = n())
write_csv(alsobrooks_large_data, "alsobrooks_large.csv")

A5: Here we see that Hogan receives signficantly more money in general in comparison to Alsobrooks. Additionally, Hogan receives many more large donations (in excess of millions) in comparison to Alsobrooks. Whereas, the small donations are around 600,000 more. This shows that either Hogan has a larger “fan base” of people who donate to him or perhaps his electorate is just wealthier. Additionally,it would be interesting to note whether Hogan has a higher amount of donations because he was known before.

We then looked at where these donations were from. For Hogan, his most donations came from Annapolis — his former residence and where the state legislature is located. His second most donations came from Potomac, the zip code with the highest median household income in Maryland. However, 14 different zip codes donated more to Alsobrooks than Annapolis did for Hogan. This showed Hogan thrived off of large donations in areas that Alsobrooks did reach.

We tested this using chloropleth maps to show the zip codes where the two received large donations. The map showed a lot of areas in northern Maryland he capitalized with those donations where Alsobrooks did not recieve any.

There were a few zip codes which did not register on DataWrapper when making these graphs, but they did not have more than five donations, so we did not think that it ultimately affected determining where the hot areas were for the two receiving their donations.

Map of Larry Hogan’s donations by zipcode: https://www.datawrapper.de/_/5LB3T/

Map of Angela Alsobrooks’ donations by zipcode: https://www.datawrapper.de/_/ChtAr/

Map of Angela Alsobrooks’ large donations by zipcode: https://www.datawrapper.de/_/BEuvY/

Map of Larry Hogan’s large donations by zipcode: https://www.datawrapper.de/_/6KTF3/

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKUiBOb3RlYm9vawoKI0ludHJvZHVjdGlvbgpXZSBjaG9zZSB0byBmb2N1cyBvbiByaWNoIHBlb3BsZSwgb3IgcGVvcGxlIHByZXN1bWFibHkgd2l0aCBoaWdoLWluY29tZSBqb2JzLCB0aHJvdWdob3V0IG91ciBwcm9qZWN0LiBQZW9wbGUgYXJlIGdlbmVyYWxseSByZWFsbHkgaW50ZXJlc3RlZCBpbiB0aGUgbGl2ZXMgb2YgcmljaCBwZW9wbGUsIGFuZCB0aGlzIGlzIHJlYWxseSBhcHBlYWxpbmcgdG8gcmVhZGVycywgd2hpY2ggbWFrZXMgaXQgbmV3c3dvcnRoeS4gV2Ugbm90aWNlZCB0aGlzIHNwZWNpZmljYWxseSB0aGlzIHdlZWsgaW4gdGhlIFdhbGwgU3RyZWV0IEpvdXJuYWwgd2hlcmUgbWFueSBvZiB0aGUgZnJvbnQgcGFnZSBhcnRpY2xlcyBwZXJ0YWluZWQgdG8gdGhlIGluc2lkZSBsaXZlcyBvZiB3ZWFsdGhpZXIgcGVvcGxlLCBmb3IgZXhhbXBsZSBpbnZlc3RtZW50IGJhbmtlcnMgYW5kIG1vcmUuIAoKRm9yIGV4YW1wbGUsIGluIG91ciBxdWVzdGlvbiBhYm91dCB3aGF0IHBlb3BsZSBhcmUgY29udHJpYnV0aW5nIHRvIHdpdGggdGhlaXIgc3BlY2lmaWMgam9icywgd2UgZm9jdXNlZCBvbiBDRU8ncyB3aG8gcHJlc3VtYWJseSBtYWtlIGEgZ29vZCBsaXZpbmcuIFdlIHRoZW4gbG9va2VkIGF0IHRoYXQgYWNyb3NzIERlbW9jcmF0aWMgYW5kIFJlcHVibGljYW4gcGFydGllcyB0byBub3RlIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZXNlIHR3byBncm91cHMgaW4gdGVybXMgb2YgdGhlIHNhbWUgb2NjdXBhdGlvbi4gCgojUHJvY2VzcyBhbmQgUXVlc3Rpb24gRGV2ZWxvcG1lbnQKRG8gbW9yZSBNYXJ5bGFuZGVycyBkb25hdGUgdG8gaW4tc3RhdGUgb3Igb3V0LW9mLXN0YXRlIGNhbmRpZGF0ZXM/IFdoaWNoIG91dC1vZi1zdGF0ZSBjYW5kaWRhdGVzIHJlY2VpdmVkIHRoZSBtb3N0IGRvbmF0aW9ucyBhbmQgdGhlIGdyZWF0ZXN0IGFtb3VudCBvZiBtb25leSBpbiBkb25hdGlvbnM/IFdoYXQgYXJlIHRoZSBkZW1vZ3JhcGhpY3Mgb2YgcGVvcGxlIHdobyBkb25hdGUgdG8gTWFyeWxhbmQgcmFjZXMgb25seT8gVG8gb3V0LW9mLXN0YXRlIHJhY2VzIG9ubHk/ICh1c2luZyB2b3RlciBkYXRhIE9SIHVzaW5nIGNlbnN1cyBkYXRhIHRvIHNlZSBpZiB0aG9zZSBkb25vcnMgYXJlIGNsdXN0ZXJlZCBpbiBzcGVjaWZpYyBsb2NhdGlvbnMsIGFuZCwgaWYgdGhleSBhcmUsIHdoYXQgdGhlIG1ha2V1cHMgb2YgdGhvc2UgbG9jYXRpb25zIGFyZSkgQW1vbmcgdG9wIE1hcnlsYW5kIGRvbm9ycywgd2hhdCBwcm9mZXNzaW9ucyBkb25hdGUgdGhlIG1vc3QgbW9uZXkgdG8gc2VuYXRlIGNhbXBhaWducz8gV2hhdOKAmXMgdGhlIG1ha2V1cCBvZiBkb25hdGlvbnMgcmVjZWl2ZWQgYnkgSG9nYW4gYW5kIEFsc29icm9va3M/IFdoYXQgcGVyY2VudGFnZSBvZiB0aGVpciBvdmVyYWxsIGRvbmF0aW9ucyB3ZXJlIGxhcmdlIGFtb3VudHMgb2YgbW9uZXkgKHRvIGJlIGRlZmluZWQsIGJ1dCA+ICQxMDAwLCBmb3IgZXhhbXBsZSkgdnMgc21hbGwgYW1vdW50cz8gV2hpY2ggcGFydHkgdGVuZHMgdG8gZG9uYXRlIG1vcmUgdG8gb3V0LW9mLXN0YXRlIHJhY2VzPwoKV2UgcmVmaW5lZCBvdXIgZGF0YXNldCBmdXJ0aGVyIGJ5IGRlY2lkaW5nIHRvIGZvY3VzIG9uIGluZGl2aWR1YWwgZG9uYXRpb25zIG1hZGUgdG8gc2VuYXRlIHJhY2VzIGJ5IGRvbm9ycyBpbiBNYXJ5bGFuZC4gVXNpbmcgdGhlIEZFQyB3ZWJzaXRlLCB3ZSBkb3dubG9hZGVkIGEgZGF0YXNldCB0aGF0IG1ldCB0aG9zZSBwYXJhbWV0ZXJzLiBXZSBjbGVhbmVkIHRoZSBkYXRhIGJ5IHJlbW92aW5nIHVubmVjZXNzYXJ5IGNvbHVtbnMgYW5kIHJlbmFtaW5nIHRoZSBjb2x1bW5zIHdoZXJlIG5lZWRlZC4gV2UgZGVjaWRlZCB0byBsaW1pdCBvdXIgZGF0YXNldCB0byBkb25hdGlvbnMgbWFkZSBpbiAyMDI0IHRvIGVuc3VyZSB0aGF0IHRoZSBkb25hdGlvbnMgd2VyZSBtYWRlIGFmdGVyIHRoZSBwcmltYXJ5IGFuZCBiZWZvcmUgdGhlIGdlbmVyYWwgZWxlY3Rpb24uIFdlIGhvcGVkIHRoaXMgd291bGQgbGltaXQgdGhlIG51bWJlciBvZiBjYW5kaWRhdGVzIHJlY2VpdmluZyBkb25hdGlvbnMsIHNpbmNlIG1vc3QgZG9uYXRpb25zIHdpbGwgZ28gdG93YXJkIG9uZSBvZiB0d28gcGFydGllcyBpbiBhIGNlcnRhaW4gcmFjZS4KCldlIGFsc28gY2xlYW5lZCB0aGUgZGF0YS1zZXQsIHJlbW92aW5nIG51bWVyb3VzIGNvbHVtbnMgdGhhdCB3ZXJlIE4vQSBvciB0aGF0IHdlcmUgdW5uZWNlc3NhcnkgdG8gb3VyIHByb2plY3QuIFdlIGFsc28gaGFkIHRvIHJlbmFtZSBvbmUgY29sdW1uIGFuZCB0aGVuIGZpbHRlciB0byBvbmx5IHRoZSByZXBvcnQgeWVhciBvZiAyMDI0IHRvIGxpbWl0IHRoZSBkb25hdGlvbnMuIFRvIGFuc3dlciBvdXIgZmlyc3QgcXVlc3Rpb24gYW5kIHF1ZXN0aW9ucywgd2XigJlsbCBuZWVkIHRvIHNlcGFyYXRlIHRoZSBvdXQtb2Ytc3RhdGUgY2FuZGlkYXRlcyBmcm9tIGluLXN0YXRlIGNhbmRpZGF0ZXMsIGJ1dCB0aGlzIHNob3VsZCBiZSBlYXN5IGJlY2F1c2Ugd2XigJlsbCBqdXN0IG5lZWQgdG8gZmlsdGVyIG91dCB0aGUgSG9nYW4gYW5kIEFsc29icm9va3MgY29tbWl0dGVlcyB0byBpc29sYXRlIHRoZSBvdXQtb2Ytc3RhdGUgY2FuZGlkYXRlcyByZWNlaXZpbmcgZG9uYXRpb25zLiBUbyBhbnN3ZXIgb3VyIHRoaXJkIHF1ZXN0aW9uLCB3ZeKAmWxsIG5lZWQgdG8gZWl0aGVyIGNhbGwgaW4gZGVtb2dyYXBoaWMgZGF0YSBmcm9tIHRoZSBjZW5zdXMgb3IgaWRlbnRpZnkgc3BlY2lmaWMgbG9jYXRpb25zIHdpdGggY2x1c3RlcnMgb2YgZG9uYXRpb25zLCBhbmQgbG9vayB1cCBpbmZvcm1hdGlvbiBhYm91dCB0aG9zZSBsb2NhdGlvbnMgdXNpbmcgY2Vuc3VzLmdvdi4gUXVlc3Rpb24gIzQgYXNrcyBhYm91dCB0aGUgcHJvZmVzc2lvbnMgb2YgZG9ub3JzLiBPbmUgb2J2aW91cyBjaGFsbGVuZ2UgaGVyZSBpcyB0aGF0IHByb2Zlc3Npb25zIGFuZCB0aXRsZXMgYXJlIG9mdGVuIHNwZWxsZWQgb3Igd29yZGVkIGRpZmZlcmVudGx5LCBldmVuIGlmIHRoZXkgZGVzY3JpYmUgdGhlIHNhbWUgam9i4oCTIGluIHRoaXMgZGF0YXNldCwgdGhlcmUgYXJlIDIsNzM4IGRpZmZlcmVudCBwcm9mZXNzaW9ucyBsaXN0ZWQuIFRvIHRyeSB0byBjb21iYXQgdGhpcywgd2Ugd2lsbCB1c2Ugb3BlbiByZWZpbmUgdG8gcmVkdWNlIHRoZSBudW1iZXIgb2Ygb2NjdXBhdGlvbnMgd2l0aCBzbGlnaHQgdmFyaWF0aW9ucyBpbiBzcGVsbGluZyBvciB3b3JkaW5nLiBXZSB3aWxsIHRoZW4gbGltaXQgdGhlIGRhdGEgc2V0IHRvIHRoZSB0b3AgMTAwMCBkb25hdGlvbnMgdG8gbWFrZSB0aGUgYW5hbHlzaXMgbW9yZSBtYW5hZ2VhYmxlLiBXZSBjYW4gYWxzbyBtYWtlIHNvbWUgYnJvYWQgb2JzZXJ2YXRpb25zIGFib3V0IHRoZSBkYXRhIGV2ZW4gd2l0aG91dCBjbGVhbmluZyB0aGUgcHJvZmVzc2lvbnPigJMgZm9yIGV4YW1wbGUsIGl04oCZcyBvYnZpb3VzIHRoYXQgdGhlIG1vc3QgZG9uYXRpb25zIGNvbWUgZnJvbSBwZW9wbGUgd2hvIHNheSB0aGV5IGFyZSDigJxub3QgZW1wbG95ZWTigJ0gKDQ1NTY4KSBvciDigJxyZXRpcmVk4oCdICgyOTQyNCkuIFRoZSBuZXh0IGhpZ2hlc3QgbnVtYmVyIG9mIGRvbmF0aW9ucyBpcyBtb3JlIHRoYW4gMjAsMDAwIGRvbmF0aW9ucyBsZXNzIHRoYW4gdGhlIG51bWJlciBvZiBkb25hdGlvbnMgZnJvbSByZXRpcmVkIHBlb3BsZS4gVGhlcmUgYXJlIDIwNyBjb21taXR0ZWVzIHRoYXQgcmVjZWl2ZWQgZG9uYXRpb25zIGluIG91ciBkYXRhc2V0LiBPcmlnaW5hbGx5LCB3ZSB0aG91Z2h0IHRoYXQgd2Ugd291bGQgdXNlIG9wZW4gcmVmaW5lIHRvIHNvcnQgdGhlIGNvbW1pdHRlZXMgYnkgY2FuZGlkYXRlIG5hbWUsIGJlY2F1c2Ugd2UgdGhvdWdodCB0aGF0IG1vc3QgY2FuZGlkYXRlcyB3b3VsZCBoYXZlIG11bHRpcGxlIGNvbW1pdHRlZXMuIEhvd2V2ZXIsIHdlIGZvdW5kIHRoYXQgdGhpcyB3YXMgYWN0dWFsbHkgcHJldHR5IHVuY29tbW9uLCBhbmQgd2Ugd2VyZW7igJl0IGFibGUgdG8gbWF0Y2ggYW55IG5hbWVzIHVzaW5nIG9wZW4gcmVmaW5lLCBvbmNlIHdlIHVwbG9hZGVkIG91ciBjc3YuIEZvciBxdWVzdGlvbiA1LCB3ZSBuZWVkIHRvIGRvIHNvbWUgZXh0cmEgcmVzZWFyY2ggdG8gZGVjaWRlIGhvdyB0byBkZWZpbmUgbGFyZ2UgZG9uYXRpb25zLiBPbmUgaWRlYSBpcyB0byBmaW5kIHRoZSBhdmVyYWdlIG9yIG1lZGlhbiBkb25hdGlvbiwgYW5kIHVzZSB0aGF0IG51bWJlciBhcyB0aGUgZGl2aWRpbmcgbGluZSBiZXR3ZWVuIHNtYWxsIGFuZCBsYXJnZSBkb25hdGlvbnMuIEFub3RoZXIgaWRlYSBpcyB0byBzZWUgaWYgdGhlcmXigJlzIGFuIGFncmVlZC11cG9uIGRlZmluaXRpb24gYnkgcGVvcGxlIHdobyB3b3JrIG9uIGNhbXBhaWducyBvciBwb2xpdGljYWwgc2NpZW50aXN0cy4gV2UgaGF2ZSBvbmUgZXh0cmEgcXVlc3Rpb24gKCM2KSB0aGF0IHdlIHdvdWxkIGxpa2UgdG8gYW5zd2VyLCBidXQgaXQgaW52b2x2ZXMgcGFydHktbGV2ZWwgYW5hbHlzaXMgdGhhdCB3ZSBjYW7igJl0IGRvIGJlY2F1c2Ugd2UgZG9u4oCZdCBoYXZlIHRoZSBwb2xpdGljYWwgcGFydGllcyBvZiB0aGUgZG9ub3JzIGluIHRoaXMgZGF0YSwgYW5kIHdlIGRvbuKAmXQgaGF2ZSBhbnl0aGluZyBpbiB0aGlzIGRhdGEgdGhhdCBpbmRpY2F0ZXMgdGhlIHBhcnR5IG9mIGVhY2ggY2FuZGlkYXRlLiBUbyBhbnN3ZXIgdGhpcyBxdWVzdGlvbiwgd2UgY291bGQgam9pbiB0aGlzIGRhdGEgd2l0aCBkYXRhIGZyb20gQWN0IEJsdWUgYW5kIFdpbiBSZWQgdG8gaWRlbnRpZnkgdGhlIHBhcnRpZXMgb2YgZG9ub3JzLiBXZSBjYW4gYWxzbyBwcm9iYWJseSBmaW5kIGEgZGF0YXNldCB3aXRoIHRoZSBjb21taXR0ZWUgbmFtZXMgYW5kIHBhcnRpZXMgZm9yIGFsbCBzZW5hdGUgY2FuZGlkYXRlcywgYW5kIGpvaW4gdGhhdCBkYXRhc2V0IHdpdGggb3Vycy4KCiNSZWZpbmluZyB0aGUgZGF0YXNldAoKYGBge3J9Cm9wdGlvbnMoc2NpcGVuPTk5OSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KGRwbHlyKQoKCmBgYAoKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoJ3RpZHljZW5zdXMnKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHRpZHljZW5zdXMpCmBgYAoKCgpgYGB7cn0KbWRfc2VuYXRlX2NvbnRyaWJ1dGlvbnMgPC0gcmVhZF9jc3YoImRhdGEvbWRfc2VuYXRlX2NvbnRyaWJ1dGlvbnMuY3N2IikgfD4KCgpwcmludChjb2x1bW5fbmFtZXMpCmBgYAoKY2xlYW4gZGF0YToKYGBge3J9CmNsZWFuZWRfZGF0YSA8LSBtZF9zZW5hdGVfY29udHJpYnV0aW9ucyB8PgoKc2VsZWN0KCAtdW51c2VkX2NvbnRicl9pZCwgLWNvbW1pdHRlZV9uYW1lLi4uOSwgLXJlY2lwaWVudF9jb21taXR0ZWVfb3JnX3R5cGUsIC1jb250cmlidXRvcl9zdWZmaXgsIC1jb250cmlidXRvcl9zdHJlZXRfMiwgLWNvbnRyaWJ1dG9yX2lkLCAtbWVtb19jb2RlLCAtbWVtb19jb2RlX2Z1bGwsIC1jYW5kaWRhdGVfaWQsIC1jYW5kaWRhdGVfbmFtZSwgLWNhbmRpZGF0ZV9maXJzdF9uYW1lLCAtY2FuZGlkYXRlX2xhc3RfbmFtZSwgLWNhbmRpZGF0ZV9taWRkbGVfbmFtZSwgLWNhbmRpZGF0ZV9wcmVmaXgsIC1jYW5kaWRhdGVfc3VmZml4LCAtY2FuZGlkYXRlX29mZmljZSwgLWNhbmRpZGF0ZV9vZmZpY2VfZnVsbCwgLWNhbmRpZGF0ZV9vZmZpY2Vfc3RhdGUsIC1jYW5kaWRhdGVfb2ZmaWNlX3N0YXRlX2Z1bGwsIC1jYW5kaWRhdGVfb2ZmaWNlX2Rpc3RyaWN0LCAtY29uZHVpdF9jb21taXR0ZWVfaWQsIC1jb25kdWl0X2NvbW1pdHRlZV9uYW1lLCAtY29uZHVpdF9jb21taXR0ZWVfc3RyZWV0MSwgLWNvbmR1aXRfY29tbWl0dGVlX3N0cmVldDIsIC1jb25kdWl0X2NvbW1pdHRlZV9jaXR5LCAtY29uZHVpdF9jb21taXR0ZWVfc3RhdGUsIC1jb25kdWl0X2NvbW1pdHRlZV96aXAsIC1kb25vcl9jb21taXR0ZWVfbmFtZSwgLW5hdGlvbmFsX2NvbW1pdHRlZV9ub25mZWRlcmFsX2FjY291bnQsIC1lbGVjdGlvbl90eXBlX2Z1bGwsIC1pbmNyZWFzZWRfbGltaXQsIC1pc19pbmRpdmlkdWFsKSB8PgoKcmVuYW1lKGNvbW1pdHRlZV9uYW1lID0gYGNvbW1pdHRlZV9uYW1lLi4uMmApIHw+CgpmaWx0ZXIocmVwb3J0X3llYXIgPT0gMjAyNCkKCgpjb2xuYW1lcyhjbGVhbmVkX2RhdGEpCmBgYAoKYGBge3J9Cm1vc3RfY29udHJpYnV0aW9ucyA8LSBjbGVhbmVkX2RhdGEgfD4Kc2VsZWN0KGNvbW1pdHRlZV9uYW1lLCBjb250cmlidXRpb25fcmVjZWlwdF9hbW91bnQpIHw+Cmdyb3VwX2J5KGNvbW1pdHRlZV9uYW1lKSB8PgpzdW1tYXJpemUodG90YWxfY29udHJpYnV0aW9uID0gc3VtKGNvbnRyaWJ1dGlvbl9yZWNlaXB0X2Ftb3VudCwgbmEucm0gPSBUUlVFKSkgfD4KYXJyYW5nZShkZXNjKHRvdGFsX2NvbnRyaWJ1dGlvbikpIApjbGVhbmVkX2RhdGEgfD4KICAKd3JpdGVfY3N2KCJkYXRhL2NsZWFuZWRfbWRfc2VuYXRlX2NvbnRyaWJ1dGlvbnMuY3N2IikKZGF0YSA8LSByZWFkX2RlbGltKCJkYXRhL2NjbC50eHQiLCBkZWxpbSA9ICJ8IiwgY29sX25hbWVzID0gRkFMU0UpCgpjbGVhbmVkX2RhdGFfY2NsIDwtIGRhdGEgfD4KcmVuYW1lKGNhbmRpZGF0ZV9pZCA9IFgxLGNhbmRpZGF0ZV9lbGVjdGlvbl95ZWFyID0gWDIsIGZlY19lbGVjdGlvbl95ZWFyID0gWDMsIGNvbW1pdHRlZV9pZCA9IFg0LGNvbW1pdHRlZV90eXBlID0gWDUsY29tbWl0dGVlX2Rlc2lnbiA9IFg2LCBsaW5rYWdlX2lkID0gWDcpCmBgYAoKaW5uZXIgam9pbiB0aGUgY29tbWl0dGVlIGlkIGFuZCBmZWMgZWxlY3Rpb24geWVhcjogCgpgYGB7cn0Kam9pbmVkX2RhdGEgPC0gaW5uZXJfam9pbihjbGVhbmVkX2RhdGEsIGNsZWFuZWRfZGF0YV9jY2wsIGJ5ID0gYygiY29tbWl0dGVlX2lkIiwgImZlY19lbGVjdGlvbl95ZWFyIikpCmBgYAoKaW5zZXJ0IHRoZSBjYW5kaWRhdGUgZGF0YToKYGBge3J9CmNhbmRpZGF0ZV9kYXRhIDwtIHJlYWRfZGVsaW0oImRhdGEvY2FuZGlkYXRlX2RhdGEudHh0IiwgZGVsaW0gPSAifCIsIGNvbF9uYW1lcyA9IEZBTFNFKSB8PgogIHNlbGVjdChYMSwgWDIsIFgzLCBYNSkgfD4gCiByZW5hbWUoY2FuZGlkYXRlX2lkID0gWDEsIGNhbmRpZGF0ZV9uYW1lID0gWDIsIGNhbmRpZGF0ZV9wYXJ0eSA9IFg1KQoKY2FuZGlkYXRlX2pvaW5lZF9kYXRhIDwtIGlubmVyX2pvaW4oam9pbmVkX2RhdGEsIGNhbmRpZGF0ZV9kYXRhLCBieSA9YygiY2FuZGlkYXRlX2lkIikpCmBgYAppbmNsdWRlIG9ubHkgdGhlc2Ugcm93czogCgpgYGB7cn0KCmZpbmFsX2RhdGEgPC0gY2FuZGlkYXRlX2pvaW5lZF9kYXRhIHw+CnNlbGVjdChjb21taXR0ZWVfaWQsIGNvbW1pdHRlZV9uYW1lLCByZXBvcnRfeWVhciwgZW50aXR5X3R5cGVfZGVzYyxjb250cmlidXRvcl9wcmVmaXgsIGNvbnRyaWJ1dG9yX25hbWUsIGNvbnRyaWJ1dG9yX2ZpcnN0X25hbWUsIGNvbnRyaWJ1dG9yX2xhc3RfbmFtZSwgY29udHJpYnV0b3JfbWlkZGxlX25hbWUsIGNvbnRyaWJ1dG9yX3N0cmVldF8xLGNvbnRyaWJ1dG9yX2NpdHksIGNvbnRyaWJ1dG9yX3N0YXRlLCBjb250cmlidXRvcl96aXAsY29udHJpYnV0b3JfZW1wbG95ZXIsIGNvbnRyaWJ1dG9yX29jY3VwYXRpb24sIGNvbnRyaWJ1dGlvbl9yZWNlaXB0X2RhdGUsIGNvbnRyaWJ1dGlvbl9yZWNlaXB0X2Ftb3VudCwgY29udHJpYnV0b3JfYWdncmVnYXRlX3l0ZCxtZW1vX3RleHQscGRmX3VybCwgY2FuZGlkYXRlX2lkLCBjYW5kaWRhdGVfZWxlY3Rpb25feWVhcikKYGBgCgojUXVlc3Rpb24gMTogRG8gbW9yZSBNYXJ5bGFuZGVycyBkb25hdGUgdG8gaW4tc3RhdGUgb3Igb3V0LW9mLXN0YXRlIGNhbmRpZGF0ZXM/IAoKYGBge3J9CmZpbmFsX2RhdGFfc3RhdGVzIDwtIGZpbmFsX2RhdGEgfD4KICBtdXRhdGUoc3RhdGUgPSBzdWJzdHIoY2FuZGlkYXRlX2lkLCAzLCA0KSkKYGBgCgpgYGB7cn0Kc3RhdGVfY29udHJpYnV0aW9ucyA8LSBmaW5hbF9kYXRhX3N0YXRlcyB8PgogIGdyb3VwX2J5KHN0YXRlKSB8PgogIHN1bW1hcmlzZSgKICAgIGNvbnRyaWJ1dGlvbl9jb3VudCA9IG4oKSwgICAgICAgICAgICAgICAgCiAgICB0b3RhbF9jb250cmlidXRpb24gPSBzdW0oY29udHJpYnV0aW9uX3JlY2VpcHRfYW1vdW50LCBuYS5ybSA9IFRSVUUpICAKICApCmBgYAoKYGBge3J9Cm1hcnlsYW5kX2NvbnRyaWIgPC0gc3RhdGVfY29udHJpYnV0aW9ucyB8PgogIGZpbHRlcihzdGF0ZSA9PSAiTUQiKSB8PgogIHN1bW1hcmlzZSh0b3RhbF9tYXJ5bGFuZF9jb250cmliID0gc3VtKHRvdGFsX2NvbnRyaWJ1dGlvbiwgbmEucm0gPSBUUlVFKSkKCgpvdGhlcl9zdGF0ZXNfY29udHJpYiA8LSBzdGF0ZV9jb250cmlidXRpb25zIHw+CiAgZmlsdGVyKHN0YXRlICE9ICJNRCIpIHw+CiAgc3VtbWFyaXNlKHRvdGFsX290aGVyX3N0YXRlc19jb250cmliID0gc3VtKHRvdGFsX2NvbnRyaWJ1dGlvbiwgbmEucm0gPSBUUlVFKSkKCgpjb21wYXJpc29uIDwtIGRhdGEuZnJhbWUoCiAgc3RhdGUgPSBjKCJNYXJ5bGFuZCIsICJPdGhlciBTdGF0ZXMiKSwKICB0b3RhbF9jb250cmlidXRpb24gPSBjKG1hcnlsYW5kX2NvbnRyaWIkdG90YWxfbWFyeWxhbmRfY29udHJpYiwgb3RoZXJfc3RhdGVzX2NvbnRyaWIkdG90YWxfb3RoZXJfc3RhdGVzX2NvbnRyaWIpCikKYGBgCgoKI0Fuc3dlciAxOgpNb250YW5hIHdhcyBzdXJwcmlzaW5nbHkgdGhlIHN0YXRlIHdpdGggdGhlIG1vc3QgY29udHJpYnV0aW9ucyAtLS0gZWRnaW5nIE1hcnlsYW5kIHdpdGggMjAyIG1vcmUgY29udHJpYnV0aW9ucyBNYXJ5bGFuZC4gTWFyeWxhbmQgcmVjZWl2ZWQgdGhlIGhpZ2hlc3QgdG90YWwgc3VtIGhvd2V2ZXIsIHdpdGggJDUsNTE2LDU2Mi4yNC4gVGhhdCB3YXMgJDQsMDc0LDIyNi4wNyBtb3JlIHRoYW4gdGhlIG5leHQgaGlnaGVzdCBzdGF0ZSwgd2hpY2ggd2FzIE1vbnRhbmEuIFRoZSBvdGhlciBzdGF0ZXMgY29tYmluZWQgcmVjaWV2ZWQgJDcsMzU4LDk3MCwgYSBsaXR0bGUgbGVzcyB0aGFuIHR3byBtaWxsaW9uIG1vcmUgdGhhbiBqdXN0IHRoZSBzdGF0ZSBvZiBNYXJ5bGFuZC4gCgoKI1F1ZXN0aW9uIDI6IFdoaWNoIG91dC1vZi1zdGF0ZSBjYW5kaWRhdGVzIHJlY2VpdmVkIHRoZSBtb3N0IGRvbmF0aW9ucyBhbmQgdGhlIGdyZWF0ZXN0IGFtb3VudCBvZiBtb25leSBpbiBkb25hdGlvbnM/IAoKYGBge3J9CmZpbmFsX2RhdGFfbm9fbWQgPC0gZmluYWxfZGF0YV9zdGF0ZXMgfD4KICBmaWx0ZXIoc3RhdGUgIT0gIk1EIikKYGBgCgpgYGB7cn0KY29udHJpYnV0aW9uc19zdW1tYXJ5IDwtIGZpbmFsX2RhdGFfbm9fbWQgfD4KICBncm91cF9ieShjb21taXR0ZWVfbmFtZSkgfD4KICBzdW1tYXJpc2UoCiAgICBudW1fY29udHJpYnV0aW9ucyA9IG4oKSwgCiAgICB0b3RhbF9jb250cmlidXRpb25fc3VtID0gc3VtKGNvbnRyaWJ1dGlvbl9yZWNlaXB0X2Ftb3VudCwgbmEucm0gPSBUUlVFKSAKICApIHw+CiAgYXJyYW5nZShjb21taXR0ZWVfbmFtZSkKYGBgCgojQW5zd2VyIDI6IApKb24gVGVzdGVyLCBTaGVycm9kIEJyb3duIGFuZCBSdWJlbiBHYWxsZWdvIHdlcmUgdGhlIHRvcCB0aHJlZSBpbiB0aGF0IG9yZGVyIGZvciBib3RoIGNvbnRyaWJ1dGlvbnMgcmVjaWV2ZWQgYW5kIHRoZSBzdW0gb2YgY29udHJpYnV0aW9ucy4gVGhlIHNpbWlsYXJpdHkgYmV0d2VlbiB0aGUgdGhyZWUgb2YgdGhlbSBpcyB0aGF0IHRoZXkgd2VyZSBhbGwgRGVtb2NyYXRzIHJ1bm5pbmcgaW4gdGlnaHRseSBjb250ZXN0ZWQgcmFjZXMuIEJyb3duIHdhcyB0aGUgb25seSB0byBsb3NlIGhpcyByYWNlLiBKb24gVGVzdGVyIGFsc28gY2xlYXJseSByZWNpZXZlZCBtb3JlIHRoYW4gdGhlIG90aGVyIHR3byAtLS0gd2hpY2ggZXhwbGFpbnMgd2h5IE1vbnRhbmEgcmVjZWl2ZWQgc28gbWFueSBtb3JlIG91dC1vZi1zdGF0ZSBjb250cmlidXRpb25zIHRoYW4gb3RoZXIgc3RhdGVzLiBNb250YW5hbnMgZm9yIFRlc3RlciByZWNlaXZlZCAzLDY3OSBtb3JlIGNvbnRyaWJ1dGlvbnMgYW5kICQyMzgsMDE3Ljk2IG1vcmUgaW4gdG90YWwgY29udHJpYnV0aW9ucyBjb21wYXJlZCB0byBGcmllbmRzIG9mIFNoZXJyb2QgQnJvd24uCgpXZSBjaG9zZSBub3QgdG8gZm9jdXMgb24gcmVmaW5pbmcgYW5kIGNvZGluZyB0aGlzIHF1ZXN0aW9uLiBMb29raW5nIGF0IGRvbmF0aW9ucyB0byBjYW5kaWRhdGVzIGxpa2UgVGVzdGVyIGFuZCBBbHNvYnJvb2tzIHdvdWxkIG5vdCBhbnN3ZXIgb3VyIG5ld3N3b3J0aGluZXNzIGJlY2F1c2Ugb3VyIGZvY3VzIGlzIG9uIHdoZXJlIHdlYWx0aHkgaW5kaXZpZHVhbHMnIG1vbmV5IGdvZXMgaW4gZWxlY3Rpb25zLiBXaGlsZSBpdCBpcyBpbnRlcmVzdGluZyB0aGF0IG91dC1vZi1zdGF0ZSBkb25hdGlvbnMgYWxpZ24gd2l0aCBjb21wZXRpdGl2ZSBTZW5hdGUgcmFjZXMsIHN1Y2ggYXMgTW9udGFuYSBhbmQgT2hpbywgdGhpcyBhbmFseXNpcyB3b3VsZCBwcmltYXJpbHkgaGlnaGxpZ2h0IGdlb2dyYXBoaWMgZG9uYXRpb24gdHJlbmRzIHJhdGhlciB0aGFuIHRoZSBiZWhhdmlvciBvZiBoaWdoLWluY29tZSBkb25vcnMuIE91ciBzdG9yeSBhaW1zIHRvIGRpc3BsYXkgaG93IHdlYWx0aHkgaW5kaXZpZHVhbHMsIHBhcnRpY3VsYXJseSBDRU9zLCB1c2UgdGhlaXIgZmluYW5jaWFsIHBvd2VyIGluIGVsZWN0aW9uIGRvbmF0aW9ucy4gRXhhbWluaW5nIHNwZWNpZmljIGNhbmRpZGF0ZXMgb3Igb3V0LW9mLXN0YXRlIGRvbmF0aW9ucyB3b3VsZCBub3QgcHJvdmlkZSB0aGUgc2FtZSBpbnNpZ2h0IGludG8gdGhlIHJvbGUgb2Ygd2VhbHRoIGFuZCBsZWFkZXJzaGlwIHBvc2l0aW9ucyBpbiBzaGFwaW5nIG92ZXJhbGwgZG9uYXRpb24gcGF0dGVybnMsIHdoaWNoIG1ha2VzIG91ciBmaW5kaW5ncyBuZXdzd29ydGh5LgoKSG93ZXZlciwgdGhlIG90aGVyIHF1ZXN0aW9ucyB3ZSBleHBsb3JlZCBwcm92aWRlZCB3ZXJlIG1vcmUgcmVsZXZhbnQuIEJ5IGZvY3VzaW5nIG9uIENFT3MsIHdlIHdlcmUgYWJsZSB0byBzaG93IHBhdHRlcm5zIG9mIGZpbmFuY2lhbCBpbmZsdWVuY2UgcmVzb25hdGluZyB3aXRoIHB1YmxpYyBpbnRlcmVzdC4gUGVvcGxlIGFyZSBvZnRlbiBtb3JlIGN1cmlvdXMgYWJvdXQgaG93IHdlYWx0aHkgaW5kaXZpZHVhbHMgc3BlbmQgdGhlaXIgbW9uZXksIGVzcGVjaWFsbHkgaW4gdGhlIGNvbnRleHQgb2YgZWxlY3Rpb25zLCBhbmQgd2UgZmVsdCB0aGF0IHRoZSBvdGhlciBxdWVzdGlvbnMgd2UgZXhwbG9yZWQgcHJvdmlkZWQgbW9yZSByZWxldmFudCBpbnNpZ2h0cyB0aGF0IGFsaWduZWQgd2l0aCBvdXIgcG90ZW50aWFsIG5ld3Mgc3RvcnkgYWJvdXQgd2hlcmUgd2VhbHRoeSBpbmRpdmlkdWFsc+KAmSBtb25leSBnb2VzIGluIGVsZWN0aW9ucy4KCiNRdWVzdGlvbiAzOiBXaGF0IGFyZSB0aGUgZGVtb2dyYXBoaWNzIG9mIHBlb3BsZSB3aG8gZG9uYXRlIHRvIE1hcnlsYW5kIHJhY2VzIG9ubHk/IFRvIG91dC1vZi1zdGF0ZSByYWNlcyBvbmx5PwoKU3RlcCAxOiBmaW5kIHBlb3BsZSB3aG8gZG9uYXRlIHRvIE9OTFkgTWFyeWxhbmQgcmFjZXMuCmBgYHtyfQoKbWFyeWxhbmRfb25seV9kb25vcnMgPC0gZmluYWxfZGF0YV9zdGF0ZXN8PgogIGdyb3VwX2J5KGNvbnRyaWJ1dG9yX25hbWUpIHw+ICAgICAgICAgICAgICAgIAogIHN1bW1hcml6ZShvbmx5X21kID0gYWxsKHN0YXRlID09ICJNRCIpKSB8PgogIGZpbHRlcihvbmx5X21kKSB8PiAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgc2VsZWN0KGNvbnRyaWJ1dG9yX25hbWUpICAgICAgICAgICAgICAgICAgICAKCmZ1bGxfbWFyeWxhbmRfb25seV9kb25vcnMgPC0gZmluYWxfZGF0YV9zdGF0ZXMgfD4KICAgZmlsdGVyKGNvbnRyaWJ1dG9yX25hbWUgJWluJSBtYXJ5bGFuZF9vbmx5X2Rvbm9ycyRjb250cmlidXRvcl9uYW1lKQoKCnppcHNfZnVsbF9tYXJ5bGFuZF9vbmx5X2Rvbm9ycyA8LSBmdWxsX21hcnlsYW5kX29ubHlfZG9ub3JzIHw+CiAgZ3JvdXBfYnkoY29udHJpYnV0b3JfbmFtZSwgY29udHJpYnV0b3JfemlwKSB8PgogIHN1bW1hcml6ZSgKICAgIG51bV9kb25hdGlvbnMgPSBuKCksICAgICAgICAgICAgIAogICAgdG90YWxfZG9uYXRlZCA9IHN1bShjb250cmlidXRpb25fcmVjZWlwdF9hbW91bnQpIAogICl8PgogIGFycmFuZ2UoZGVzYyhudW1fZG9uYXRpb25zKSkgIAoKbWRfemlwX3N1bW1hcnkgPC0gemlwc19mdWxsX21hcnlsYW5kX29ubHlfZG9ub3JzIHw+CiAgZ3JvdXBfYnkoY29udHJpYnV0b3JfemlwKSB8PiAgICAgICAgICAgICAgCiAgc3VtbWFyaXplKAogICAgbnVtX2RvbmF0aW9ucyA9IG4oKSwgICAgICAgICAgICAgICAgICAgICAgCiAgICB0b3RhbF9kb25hdGVkID0gc3VtKHRvdGFsX2RvbmF0ZWQpICAgICAgCiAgKSB8PgogIGFycmFuZ2UoZGVzYyhudW1fZG9uYXRpb25zKSkgCgpgYGAKYGBge3J9CmFsbF96Y3RhIDwtIGdldF9hY3MoCiAgZ2VvZ3JhcGh5ID0gInpjdGEiLAogIHZhcmlhYmxlcyA9IGMoCiAiQjAxMDAzXzAwMSIsIAogICAgICJCMDEwMDJfMDAxIiwgIz0gIm1lZGlhbl9hZ2UiLAogIkIxOTAxM18wMDEiLCAjID0gIm1lZGlhbl9pbmNvbWUiLAogICJCMDIwMDFfMDAyIiwgIyA9ICJ3aGl0ZV9hbG9uZSIsCiAgICAgIkIwMjAwMV8wMDMiLCAjID0gImJsYWNrX2Fsb25lIiwKIkIwMjAwMV8wMDQiLCAjID0gIm5hdGl2ZV9hbWVyaWNhbiIsCiAgICAiQjAyMDAxXzAwNSIsICM9ICJhc2lhbl9hbG9uZSIsCiAiQjAyMDAxXzAwNiIsICMgPSAiaGF3YWlpYW5fcGFjaWZpYyIsCiJCMDIwMDFfMDA3IiwgICMgPSAib3RoZXJfcmFjZSIsCiAgICAiQjAyMDAxXzAwOCIsICMgPSAidHdvX29yX21vcmVfcmFjZXMiCiJCMDMwMDJfMDEyRSIpLAogIAogIHllYXIgPSAyMDIyLAogIHN1cnZleSA9ICJhY3M1IiwKICBvdXRwdXQgPSAid2lkZSIKKQoKYWxsX3pjdGEgPC0gYWxsX3pjdGEgJT4lCiAgcmVuYW1lKAogICAgdG90YWxfcG9wdWxhdGlvbiA9IEIwMTAwM18wMDFFLCAgICAgIAogICAgbWVkaWFuX2FnZSA9IEIwMTAwMl8wMDFFLCAgICAgICAgICAgIAogICAgbWVkaWFuX2luY29tZSA9IEIxOTAxM18wMDFFLCAgICAgICAgCiAgICB3aGl0ZV9hbG9uZSA9IEIwMjAwMV8wMDJFLCAgICAgICAgICAKICAgIGJsYWNrX2Fsb25lID0gQjAyMDAxXzAwM0UsICAgICAgICAgCiAgICBuYXRpdmVfYW1lcmljYW4gPSBCMDIwMDFfMDA0RSwgICAgICAKICAgIGFzaWFuX2Fsb25lID0gQjAyMDAxXzAwNUUsICAgICAgICAgIAogICAgaGF3YWlpYW5fcGFjaWZpYyA9IEIwMjAwMV8wMDZFLCAgICAgCiAgICBvdGhlcl9yYWNlID0gQjAyMDAxXzAwN0UsICAgICAgICAgICAKICAgIHR3b19vcl9tb3JlX3JhY2VzID0gQjAyMDAxXzAwOEUsCiAgICBoaXNwYW5pY19vcl9sYXRpbm8gPSBCMDMwMDJfMDEyRQogICkKCmBgYApgYGB7cn0KCnppcHNfZnVsbF9tYXJ5bGFuZF9vbmx5X2Rvbm9ycyA8LSB6aXBzX2Z1bGxfbWFyeWxhbmRfb25seV9kb25vcnMgfD4KICBtdXRhdGUoCiAgICBjb250cmlidXRvcl96aXAgPSBzdWJzdHIoY29udHJpYnV0b3JfemlwLCAxLCA1KSwKICAgIGNvbnRyaWJ1dG9yX3ppcCA9IHNwcmludGYoIiUwNXMiLCBjb250cmlidXRvcl96aXApCiAgKQoKemlwc19mdWxsX21hcnlsYW5kX29ubHlfZG9ub3JzIDwtIHppcHNfZnVsbF9tYXJ5bGFuZF9vbmx5X2Rvbm9ycyB8PgogIG11dGF0ZShjb250cmlidXRvcl96aXAgPSBhcy5jaGFyYWN0ZXIoY29udHJpYnV0b3JfemlwKSkKCm1lcmdlZF9kYXRhX21kIDwtIHppcHNfZnVsbF9tYXJ5bGFuZF9vbmx5X2Rvbm9ycyAlPiUKICBsZWZ0X2pvaW4oYWxsX3pjdGEsIGJ5ID0gYygiY29udHJpYnV0b3JfemlwIiA9ICJHRU9JRCIpKQoKc3VtbWFyaXplZF9tZF9kb25vcl9kZW1vc19ieV96aXAgPC0gbWVyZ2VkX2RhdGFfbWQgJT4lCiAgZ3JvdXBfYnkoY29udHJpYnV0b3JfemlwKSB8PgogIHN1bW1hcml6ZSgKICAgIHRvdGFsX3BvcHVsYXRpb24gPSBmaXJzdCh0b3RhbF9wb3B1bGF0aW9uKSwgIAogICAgbWVkaWFuX2FnZSA9IGZpcnN0KG1lZGlhbl9hZ2UpLCAgICAgICAgICAgICAKICAgIG1lZGlhbl9pbmNvbWUgPSBmaXJzdChtZWRpYW5faW5jb21lKSwgICAgICAgCiAgICB3aGl0ZV9hbG9uZSA9IGZpcnN0KHdoaXRlX2Fsb25lKSwgICAgICAgICAgICAKICAgIGJsYWNrX2Fsb25lID0gZmlyc3QoYmxhY2tfYWxvbmUpLCAgICAgICAgICAgCiAgICBuYXRpdmVfYW1lcmljYW4gPSBmaXJzdChuYXRpdmVfYW1lcmljYW4pLCAgIAogICAgYXNpYW5fYWxvbmUgPSBmaXJzdChhc2lhbl9hbG9uZSksICAgICAgICAgICAKICAgIGhhd2FpaWFuX3BhY2lmaWMgPSBmaXJzdChoYXdhaWlhbl9wYWNpZmljKSwgIAogICAgb3RoZXJfcmFjZSA9IGZpcnN0KG90aGVyX3JhY2UpLCAgCiAgICAgaGlzcGFuaWNfb3JfbGF0aW5vID0gZmlyc3QoaGlzcGFuaWNfb3JfbGF0aW5vKSwKICAgIHR3b19vcl9tb3JlX3JhY2VzID0gZmlyc3QodHdvX29yX21vcmVfcmFjZXMpLAogICAgbnVtX2RvbmF0aW9ucyA9IG4oKSwgICAgICAgICAgICAgICAgICAgICAgCiAgICB0b3RhbF9hbXRfZG9uYXRlZCA9IHN1bSh0b3RhbF9kb25hdGVkLCBuYS5ybSA9IFRSVUUpIAogICkgfD4gCiAgYXJyYW5nZShkZXNjKG51bV9kb25hdGlvbnMpKSAgfD4gCiAgbXV0YXRlKAogICAgd2hpdGVfYWxvbmVfcGVyX2NhcGl0YSA9IHdoaXRlX2Fsb25lIC8gdG90YWxfcG9wdWxhdGlvbiwKICAgIGJsYWNrX2Fsb25lX3Blcl9jYXBpdGEgPSBibGFja19hbG9uZSAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBuYXRpdmVfYW1lcmljYW5fcGVyX2NhcGl0YSA9IG5hdGl2ZV9hbWVyaWNhbiAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBhc2lhbl9hbG9uZV9wZXJfY2FwaXRhID0gYXNpYW5fYWxvbmUgLyB0b3RhbF9wb3B1bGF0aW9uLAogICAgaGF3YWlpYW5fcGFjaWZpY19wZXJfY2FwaXRhID0gaGF3YWlpYW5fcGFjaWZpYyAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBvdGhlcl9yYWNlX3Blcl9jYXBpdGEgPSBvdGhlcl9yYWNlIC8gdG90YWxfcG9wdWxhdGlvbiwKICAgICBoaXNwYW5pY19vcl9sYXRpbm9fcGVyX2NhcGl0YSA9IGhpc3BhbmljX29yX2xhdGlubyAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICB0d29fb3JfbW9yZV9yYWNlc19wZXJfY2FwaXRhID0gdHdvX29yX21vcmVfcmFjZXMgLyB0b3RhbF9wb3B1bGF0aW9uLAogICAgbWVkaWFuX2luY29tZV9wZXJfY2FwaXRhID0gbWVkaWFuX2luY29tZSAvIHRvdGFsX3BvcHVsYXRpb24KICApIHw+CiAgIG11dGF0ZSgKICAgIHdoaXRlX2Fsb25lX3BlcmNlbnQgPSB3aGl0ZV9hbG9uZV9wZXJfY2FwaXRhICogMTAwLAogICAgYmxhY2tfYWxvbmVfcGVyY2VudCA9IGJsYWNrX2Fsb25lX3Blcl9jYXBpdGEgKiAxMDAsCiAgICBuYXRpdmVfYW1lcmljYW5fcGVyY2VudCA9IG5hdGl2ZV9hbWVyaWNhbl9wZXJfY2FwaXRhICogMTAwLAogICAgYXNpYW5fYWxvbmVfcGVyY2VudCA9IGFzaWFuX2Fsb25lX3Blcl9jYXBpdGEgKiAxMDAsCiAgICBoYXdhaWlhbl9wYWNpZmljX3BlcmNlbnQgPSBoYXdhaWlhbl9wYWNpZmljX3Blcl9jYXBpdGEgKiAxMDAsCiAgICBvdGhlcl9yYWNlX3BlcmNlbnQgPSBvdGhlcl9yYWNlX3Blcl9jYXBpdGEgKiAxMDAsCiAgICBoaXNwYW5pY19vcl9sYXRpbm9fcGVyY2VudCA9IGhpc3BhbmljX29yX2xhdGlub19wZXJfY2FwaXRhICoxMDAsCiAgICB0d29fb3JfbW9yZV9yYWNlc19wZXJjZW50ID0gdHdvX29yX21vcmVfcmFjZXNfcGVyX2NhcGl0YSAqIDEwMCwKICAgIG1lZGlhbl9pbmNvbWVfcGVyX2NhcGl0YSA9IG1lZGlhbl9pbmNvbWVfcGVyX2NhcGl0YSAqIDEwMAogICkgfD4KIG11dGF0ZSgKICAgIGFtdF9kb25hdGVkX3Blcl9yZXNpZGVudCA9IHRvdGFsX2FtdF9kb25hdGVkIC8gdG90YWxfcG9wdWxhdGlvbgogICkgfD4gICAKICBzZWxlY3Qod2hlcmUofiAhYWxsKGlzLm5hKC4pKSkpCgoKYGBgCmBgYHtyfQoKY2xlYW5lcl9tZF9kb25vcl9kZW1vcyA8LSBzdW1tYXJpemVkX21kX2Rvbm9yX2RlbW9zX2J5X3ppcCB8PiAKICAgIHNlbGVjdCgtbWF0Y2hlcygiX3Blcl9jYXBpdGEiKSwgLSJuYXRpdmVfYW1lcmljYW4iLCAtImhhd2FpaWFuX3BhY2lmaWMiLCAtIm90aGVyX3JhY2UiLCAtInR3b19vcl9tb3JlX3JhY2VzIiwgLSJ3aGl0ZV9hbG9uZSIsIC0iYmxhY2tfYWxvbmUiLCAtImFzaWFuX2Fsb25lIikgCiAKCgpgYGAKcmVwZWF0IGZvciBvdXQgb2Ygc3RhdGUgZG9ub3JzOiAKCmBgYHtyfQpvdXRfb2Zfc3RhdGVfZG9ub3JzIDwtIGZpbmFsX2RhdGFfc3RhdGVzIHw+CiAgZmlsdGVyKHN0YXRlICE9ICJNRCIpCgp4bWFyeWxhbmRfZG9ub3JzIDwtIGZpbmFsX2RhdGFfc3RhdGVzIHw+CiAgZmlsdGVyKHN0YXRlID09ICJNRCIpCgpvdXRfb2Zfc3RhdGVfb25seV9kb25vcnMgPC0gb3V0X29mX3N0YXRlX2Rvbm9ycyB8PgogIGZpbHRlcighY29udHJpYnV0b3JfbmFtZSAlaW4lIHhtYXJ5bGFuZF9kb25vcnMkY29udHJpYnV0b3JfbmFtZSkgIHw+Cmdyb3VwX2J5KGNvbnRyaWJ1dG9yX25hbWUsIGNvbnRyaWJ1dG9yX3ppcCkgfD4KICBzdW1tYXJpemUoCiAgICBudW1fZG9uYXRpb25zID0gbigpLCAgICAgICAgICAgIAogICAgdG90YWxfZG9uYXRlZCA9IHN1bShjb250cmlidXRpb25fcmVjZWlwdF9hbW91bnQpICAKICApIHw+CiAgYXJyYW5nZShkZXNjKG51bV9kb25hdGlvbnMpKSAgCgoKb3V0X29mX3N0YXRlX29ubHlfZG9ub3JzIDwtIG91dF9vZl9zdGF0ZV9vbmx5X2Rvbm9ycyB8PgogIG11dGF0ZSgKICAgIGNvbnRyaWJ1dG9yX3ppcCA9IHN1YnN0cihjb250cmlidXRvcl96aXAsIDEsIDUpLAogICAgY29udHJpYnV0b3JfemlwID0gc3ByaW50ZigiJTA1cyIsIGNvbnRyaWJ1dG9yX3ppcCkKICApCgoKYGBgCmBgYHtyfQoKbWVyZ2VkX2RhdGFfb29zIDwtIG91dF9vZl9zdGF0ZV9vbmx5X2Rvbm9yc3w+CiAgbGVmdF9qb2luKGFsbF96Y3RhLCBieSA9IGMoImNvbnRyaWJ1dG9yX3ppcCIgPSAiR0VPSUQiKSkKCmNsZWFuZXJfb29zX2Rvbm9yX2RlbW9zIDwtIG1lcmdlZF9kYXRhX29vcyAgfD4KICAgZ3JvdXBfYnkoY29udHJpYnV0b3JfemlwKSB8PgogIHN1bW1hcml6ZSgKICAgIHRvdGFsX3BvcHVsYXRpb24gPSBmaXJzdCh0b3RhbF9wb3B1bGF0aW9uKSwgCiAgICBtZWRpYW5fYWdlID0gZmlyc3QobWVkaWFuX2FnZSksICAgICAgICAgICAgICAKICAgIG1lZGlhbl9pbmNvbWUgPSBmaXJzdChtZWRpYW5faW5jb21lKSwgICAgICAgIAogICAgd2hpdGVfYWxvbmUgPSBmaXJzdCh3aGl0ZV9hbG9uZSksICAgICAgICAgCiAgICBibGFja19hbG9uZSA9IGZpcnN0KGJsYWNrX2Fsb25lKSwgICAgICAgICAgICAKICAgIG5hdGl2ZV9hbWVyaWNhbiA9IGZpcnN0KG5hdGl2ZV9hbWVyaWNhbiksICAgCiAgICBhc2lhbl9hbG9uZSA9IGZpcnN0KGFzaWFuX2Fsb25lKSwgICAgICAgICAgIAogICAgaGF3YWlpYW5fcGFjaWZpYyA9IGZpcnN0KGhhd2FpaWFuX3BhY2lmaWMpLAogICAgb3RoZXJfcmFjZSA9IGZpcnN0KG90aGVyX3JhY2UpLCAgICAgCiAgICBoaXNwYW5pY19vcl9sYXRpbm8gPSBmaXJzdChoaXNwYW5pY19vcl9sYXRpbm8pLAogICAgdHdvX29yX21vcmVfcmFjZXMgPSBmaXJzdCh0d29fb3JfbW9yZV9yYWNlcyksCiAgICBudW1fZG9uYXRpb25zID0gbigpLCAgICAgICAgICAgICAgICAgICAgICAKICAgIHRvdGFsX2FtdF9kb25hdGVkID0gc3VtKHRvdGFsX2RvbmF0ZWQsIG5hLnJtID0gVFJVRSkgCiAgKSB8PiAKICBhcnJhbmdlKGRlc2MobnVtX2RvbmF0aW9ucykpICB8PiAKICBtdXRhdGUoCiAgIAogICAgd2hpdGVfYWxvbmVfcGVyX2NhcGl0YSA9IHdoaXRlX2Fsb25lIC8gdG90YWxfcG9wdWxhdGlvbiwKICAgIGJsYWNrX2Fsb25lX3Blcl9jYXBpdGEgPSBibGFja19hbG9uZSAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBuYXRpdmVfYW1lcmljYW5fcGVyX2NhcGl0YSA9IG5hdGl2ZV9hbWVyaWNhbiAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBhc2lhbl9hbG9uZV9wZXJfY2FwaXRhID0gYXNpYW5fYWxvbmUgLyB0b3RhbF9wb3B1bGF0aW9uLAogICAgaGF3YWlpYW5fcGFjaWZpY19wZXJfY2FwaXRhID0gaGF3YWlpYW5fcGFjaWZpYyAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBvdGhlcl9yYWNlX3Blcl9jYXBpdGEgPSBvdGhlcl9yYWNlIC8gdG90YWxfcG9wdWxhdGlvbiwKICAgIGhpc3BhbmljX29yX2xhdGlub19wZXJfY2FwaXRhID0gaGlzcGFuaWNfb3JfbGF0aW5vIC8gdG90YWxfcG9wdWxhdGlvbiwKICAgIHR3b19vcl9tb3JlX3JhY2VzX3Blcl9jYXBpdGEgPSB0d29fb3JfbW9yZV9yYWNlcyAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBtZWRpYW5faW5jb21lX3Blcl9jYXBpdGEgPSBtZWRpYW5faW5jb21lIC8gdG90YWxfcG9wdWxhdGlvbgogICkgfD4KICAgbXV0YXRlKAogIAogICAgd2hpdGVfYWxvbmVfcGVyY2VudCA9IHdoaXRlX2Fsb25lX3Blcl9jYXBpdGEgKiAxMDAsCiAgICBibGFja19hbG9uZV9wZXJjZW50ID0gYmxhY2tfYWxvbmVfcGVyX2NhcGl0YSAqIDEwMCwKICAgIG5hdGl2ZV9hbWVyaWNhbl9wZXJjZW50ID0gbmF0aXZlX2FtZXJpY2FuX3Blcl9jYXBpdGEgKiAxMDAsCiAgICBhc2lhbl9hbG9uZV9wZXJjZW50ID0gYXNpYW5fYWxvbmVfcGVyX2NhcGl0YSAqIDEwMCwKICAgIGhhd2FpaWFuX3BhY2lmaWNfcGVyY2VudCA9IGhhd2FpaWFuX3BhY2lmaWNfcGVyX2NhcGl0YSAqIDEwMCwKICAgIG90aGVyX3JhY2VfcGVyY2VudCA9IG90aGVyX3JhY2VfcGVyX2NhcGl0YSAqIDEwMCwKICAgIGhpc3BhbmljX29yX2xhdGlub19wZXJjZW50ID0gaGlzcGFuaWNfb3JfbGF0aW5vX3Blcl9jYXBpdGEgKjEwMCwKICAgIHR3b19vcl9tb3JlX3JhY2VzX3BlcmNlbnQgPSB0d29fb3JfbW9yZV9yYWNlc19wZXJfY2FwaXRhICogMTAwLAogICAgbWVkaWFuX2luY29tZV9wZXJfY2FwaXRhID0gbWVkaWFuX2luY29tZV9wZXJfY2FwaXRhICogMTAwICAKICApIAoKY2xlYW5lcl9vb3NfZG9ub3JfZGVtb3MgPC0gY2xlYW5lcl9vb3NfZG9ub3JfZGVtb3MgfD4gc2VsZWN0KC1tYXRjaGVzKCJfcGVyX2NhcGl0YSIpLCAtIm5hdGl2ZV9hbWVyaWNhbiIsIC0iaGF3YWlpYW5fcGFjaWZpYyIsIC0ib3RoZXJfcmFjZSIsIC0idHdvX29yX21vcmVfcmFjZXMiLCAtIndoaXRlX2Fsb25lIiwgLSJibGFja19hbG9uZSIsIC0iYXNpYW5fYWxvbmUiKSB8PiAKICAKICBtdXRhdGUoCiAgICBhbXRfZG9uYXRlZF9wZXJfcmVzaWRlbnQgPSB0b3RhbF9hbXRfZG9uYXRlZCAvIHRvdGFsX3BvcHVsYXRpb24KICApIHw+CiAgc2VsZWN0KHdoZXJlKH4gIWFsbChpcy5uYSguKSkpKQoKCmBgYApgYGB7cn0KaGVhZChtZXJnZWRfZGF0YV9vb3MpCmBgYAoKCmBgYHtyfQpjbGVhbmVyX29vc19kb25vcl9kZW1vcyA8LSBjbGVhbmVyX29vc19kb25vcl9kZW1vcyB8PiAgIAogIG11dGF0ZSgKICAgIGNvbnRyaWJ1dG9yX3ppcCA9IGFzLmNoYXJhY3Rlcihjb250cmlidXRvcl96aXApLCAgICAgICAgICMgRW5zdXJlIFpJUCBjb2RlcyBhcmUgc3RyaW5ncwogICAgY29udHJpYnV0b3JfemlwID0gc3RyX3BhZChjb250cmlidXRvcl96aXAsIHdpZHRoID0gNSwgICAgIyBQYWQgd2l0aCBsZWFkaW5nIHplcm9zIHRvIGVuc3VyZSA1IGRpZ2l0cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWRlID0gImxlZnQiLCBwYWQgPSAiMCIpLAogICAgY29udHJpYnV0b3JfemlwID0gaWZlbHNlKHN0cl9kZXRlY3QoY29udHJpYnV0b3JfemlwLCAiXlxcZHs1fSQiKSwgICMgS2VlcCBvbmx5IHZhbGlkIDUtZGlnaXQgWklQIGNvZGVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJpYnV0b3JfemlwLCBOQSkpICB8PgptdXRhdGUoY29udHJpYnV0b3JfemlwID0gaWZlbHNlKGNvbnRyaWJ1dG9yX3ppcCA9PSAiMzA2MzkiLCAiMjA2MzkiLCBjb250cmlidXRvcl96aXApKQoKCgpjbGVhbmVyX29vc19kb25vcl9kZW1vcyB8PiBhcnJhbmdlKGRlc2MoYW10X2RvbmF0ZWRfcGVyX3Jlc2lkZW50KSkgfD4gd3JpdGVfY3N2KCJjbGVhbmVyX29vc19kb25vcl9kZW1vcy5jc3YiKQoKYGBgCgpgYGB7cn0KY2xlYW5lcl9tZF9kb25vcl9kZW1vcyA8LSBjbGVhbmVyX21kX2Rvbm9yX2RlbW9zIHw+IAogIG11dGF0ZSgKICAgIGNvbnRyaWJ1dG9yX3ppcCA9IGFzLmNoYXJhY3Rlcihjb250cmlidXRvcl96aXApLCAgICAgICAgICMgRW5zdXJlIFpJUCBjb2RlcyBhcmUgc3RyaW5ncwogICAgY29udHJpYnV0b3JfemlwID0gc3RyX3BhZChjb250cmlidXRvcl96aXAsIHdpZHRoID0gNSwgICAgIyBQYWQgd2l0aCBsZWFkaW5nIHplcm9zIHRvIGVuc3VyZSA1IGRpZ2l0cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWRlID0gImxlZnQiLCBwYWQgPSAiMCIpLAogICAgY29udHJpYnV0b3JfemlwID0gaWZlbHNlKHN0cl9kZXRlY3QoY29udHJpYnV0b3JfemlwLCAiXlxcZHs1fSQiKSwgICMgS2VlcCBvbmx5IHZhbGlkIDUtZGlnaXQgWklQIGNvZGVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJpYnV0b3JfemlwLCBOQSkpIAoKY2xlYW5lcl9tZF9kb25vcl9kZW1vc3w+ICBhcnJhbmdlKGRlc2MoYW10X2RvbmF0ZWRfcGVyX3Jlc2lkZW50KSkgfD4gd3JpdGVfY3N2KCJjbGVhbmVyX21kX2Rvbm9yX2RlbW9zLmNzdiIpCmBgYAoKCmBgYHtyfQp0b3BfY2xlYW5lcl9tZF9kb25vcl9kZW1vcyA8LSBjbGVhbmVyX21kX2Rvbm9yX2RlbW9zIHw+CiBhcnJhbmdlKGRlc2MoYW10X2RvbmF0ZWRfcGVyX3Jlc2lkZW50KSkgfD4KICBzbGljZV9oZWFkKG4gPSA1KQoKd3JpdGVfY3N2KHRvcF9jbGVhbmVyX21kX2Rvbm9yX2RlbW9zLCAidG9wX2NsZWFuZXJfbWRfZG9ub3JfZGVtb3MuY3N2IikKCmBgYAoKYGBge3J9CnRvcF9jbGVhbmVyX29vc19kb25vcl9kZW1vcyA8LSBjbGVhbmVyX29vc19kb25vcl9kZW1vcyB8PgogICAgYXJyYW5nZShkZXNjKGFtdF9kb25hdGVkX3Blcl9yZXNpZGVudCkpIHw+CiAgc2xpY2VfaGVhZChuID0gNSkKCndyaXRlX2Nzdih0b3BfY2xlYW5lcl9vb3NfZG9ub3JfZGVtb3MsICJ0b3BfY2xlYW5lcl9vb3NfZG9ub3JfZGVtb3MuY3N2IikKYGBgCgoKI0Fuc3dlciAzOiAKCkluIGFza2luZyBhbmQgYW5zd2VyaW5nIHRoaXMgcXVlc3Rpb24sIHdlIGF0dGVtcHQgdG8gdXNlIGRvbmF0aW9ucyBhcyBhIG1lYXN1cmUgb2YgYSBkb25vcidzIHBlcmNlcHRpb24gb2YgdGhlaXIgZG9uYXRpb24ncyBhYmlsaXR5IHRvIGltcGFjdCB0aGUgb3V0Y29tZSBvZiBhIHJhY2UsIG1lYW5pbmcgdGhhdCBwZW9wbGUgd2hvIGRvbmF0ZWQgKm9ubHkqIHRvIE1hcnlsYW5kJ3Mgc2VuYXRlIHJhY2UsIHdlIGluZmVyLCBkZXRlcm1pbmVkIHRoYXQgdGhlaXIgZG9uYXRpb24gd291bGQgYmUgbW9zdCBpbXBhY3RmdWwgdG8gdGhhdCByYWNlLiBDb252ZXJzZWx5LCBwZW9wbGUgd2hvIGRvbmF0ZWQgKm9ubHkqIHRvIG91dC1vZi1zdGF0ZSByYWNlcyBkZXRlcm1pbmVkIHRoYXQgdGhlaXIgZG9uYXRpb25zIHdlcmUgbW9zdCBuZWVkZWQgdG8gaW5mbHVlbmNlIHRob3NlIHJhY2VzLiAKCkl0IGlzIGNlcnRhaW5seSBwb3NzaWJsZSB0aGF0IGRvbm9ycyBnaXZlIG1vbmV5IHRvIGNhbmRpZGF0ZXMgZm9yIG90aGVyIHJlYXNvbnMgKE1heWJlIHRoZXkncmUganVzdCBhIGJpZyBmYW4gb2YgYSBjZXJ0YWluIHBvbGl0aWNhbikuIEhvd2V2ZXIsIGdpdmVuIHRoYXQgdGhlIHRvcCBvdXQtb2Ytc3RhdGUgZG9uYXRpb25zIHdlcmUgbWFkZSB0byBjYW5kaWRhdGVzIGluIGNvbXBldGl0aXZlIHJhY2VzLCBhbmQgdGhhdCBNYXJ5bGFuZCdzIHNlbmF0ZSByYWNlIHdhcyBjb21wZXRpdGl2ZSB0aGlzIHllYXIsIHdlIHRoaW5rIHRoaXMgaXMgYSByZWFzb25hYmxlIHRoZW9yeSB0aGF0IHdlIGNhbiB1c2UgdG8gaW50ZXJwcmV0IHRoaXMgZGF0YS4gCgpUbyBpbGx1c3RyYXRlIG91ciBmaW5kaW5ncywgSSBmaXJzdCBjcmVhdGVkIHR3byBEYXRhd3JhcHBlciBtYXBzIHNob3dpbmcgZG9uYXRpb24gYW1vdW50IHBlciBjYXBpdGEgYW5kIGRlbW9ncmFwaGljIGRhdGEsIGluY2x1ZGluZyBtZWRpYW4gaW5jb21lIGFuZCBhZ2UsIGFuZCByYWNpYWwgbWFrZXVwLiAKCkluLXN0YXRlIGVsZWN0aW9uIGRvbm9yczogaHR0cHM6Ly9kYXRhd3JhcHBlci5kd2Nkbi5uZXQvOTF1WUkvMS8KT3V0LW9mLXN0YXRlIGVsZWN0aW9uIGRvbm9yczogaHR0cHM6Ly9kYXRhd3JhcHBlci5kd2Nkbi5uZXQvUGxsUUMvMS8KCkZyb20gY29tcGFyaW5nIHRob3NlIG1hcHMsIGl0J3MgY2xlYXIgdGhhdCBNYXJ5bGFuZGVycyBpbiBjZXJ0YWluIHBhcnRzIG9mIHRoZSBzdGF0ZSAodGhlIHdlYWx0aHkgREMgc3VidXJicywgQW5uYXBvbGlzLCBhbmQgcGFydHMgb2YgVGFsYm90IGNvdW50eSkgZG9uYXRlIG1vcmUgbW9uZXksIHdoZXRoZXIgdGhleSBhcmUgZG9uYXRpbmcgdG8gaW4tc3RhdGUgb3Igb3V0LW9mLXN0YXRlIGVsZWN0aW9ucy4gCgpXZSBjYW4gc2VlIHRoYXQgZm9yIE1hcnlsYW5kIHJhY2VzLCBkb25vcnMgaW4gdGhvc2UgaGlnaC1kb25hdGlvbiBhcmVhcyBkb25hdGVkIHNpbWlsYXIgYW1vdW50cyBwZXIgY2FwaXRhLiBIb3dldmVyLCBmb3Igb3V0LW9mLXN0YXRlIHJhY2VzLCB0aGUgbGFyZ2VzdCBkb25hdGlvbnMgcGVyIGNhcGl0YSB3ZXJlIGNvbmNlbnRyYXRlZCBhcm91bmQgdGhlIERDIHN1YnVyYnMuIAoKVGhpcyBpcyBsaWtlbHkgYmVjYXVzZSBNYXJ5bGFuZCdzIHdlYWx0aGllc3QgZG9ub3JzIGxpdmUgaW4gdGhhdCBwYXJ0IG9mIHRoZSBzdGF0ZSwgYW5kIG1hZGUgbGFyZ2UgZG9uYXRpb25zIHRvIGNhbmRpZGF0ZXMgSm9uIFRlc3RlciwgU2hlcnJvZCBCcm93biBhbmQgUnViZW4gR2FsbGVnbyBpbiBhbiBlZmZvcnQgdG8gc3dheSBjb21wZXRpdGl2ZSByYWNlcyBpbiBzd2luZyBzdGF0ZXMuIAoKRG9uYXRpb25zIHRvIE1hcnlsYW5kIHJhY2VzIHdlcmUgbW9yZSBldmVubHkgZGlzcGVyc2VkIGFjcm9zcyB0aGUgc3RhdGUuIFRoaXMgbWF5IGJlIGJlY2F1c2UgdGhlIE1hcnlsYW5kIHJhY2Ugd2FzIGNvbXBldGl0aXZlIHRoaXMgeWVhciwgYW5kIE1hcnlsYW5kZXJzIGZlbHQgdGhhdCB0aGVpciBjb250cmlidXRpb25zIHRvIHRoZSBpbi1zdGF0ZSBzZW5hdGUgcmFjZSBjb3VsZCBtYWtlIGEgZGlmZmVyZW5jZSBpbiBzaGFwaW5nIHRoZSBvdXRjb21lIG9mIHRoZSBlbGVjdGlvbiAoYXMgb3Bwb3NlZCB0byBtb3JlIHR5cGljYWwgZWxlY3Rpb24geWVhcnMsIHdoZXJlIE1hcnlsYW5kIGlzIGFuIHVuY29udGVzdGVkIGJsdWUgc3RhdGUpLgoKCiMgUXVlc3Rpb24gNDogQW1vbmcgdG9wIE1hcnlsYW5kIGRvbm9ycywgd2hhdCBwcm9mZXNzaW9ucyBkb25hdGUgdGhlIG1vc3QgbW9uZXkgdG8gc2VuYXRlIGNhbXBhaWducz8gCgoKYGBge3J9CmluZGl2aWR1YWxfZG9ub3JzICA8LSBmaW5hbF9kYXRhX3N0YXRlc3w+Cmdyb3VwX2J5KGNvbnRyaWJ1dG9yX25hbWUsIGNvbnRyaWJ1dG9yX3ppcCwgY29udHJpYnV0b3Jfb2NjdXBhdGlvbiwgY29udHJpYnV0b3JfZW1wbG95ZXIpIHw+IAogIHN1bW1hcml6ZSgKICAgIG51bV9kb25hdGlvbnMgPSBuKCksICAgICAgICAgICAgICAgCiAgICB0b3RhbF9kb25hdGVkID0gc3VtKGNvbnRyaWJ1dGlvbl9yZWNlaXB0X2Ftb3VudCkgCiAgKSB8PgogIGFycmFuZ2UoZGVzYyhudW1fZG9uYXRpb25zKSkgIAoKam9ic190b19jbGVhbiA8LSBpbmRpdmlkdWFsX2Rvbm9ycyB8PiAKICBncm91cF9ieShjb250cmlidXRvcl9vY2N1cGF0aW9uKSB8PgogIHN1bW1hcml6ZSgKICAgIG51bWJlcl9qb2JzID0gbigpLAogICAgbnVtX2RvbmF0aW9ucyA9IG4oKSwgIAogICAgIHRvdGFsX2RvbmF0aW9ucyA9IHN1bSh0b3RhbF9kb25hdGVkKQogICkgfD4gYXJyYW5nZShkZXNjKG51bWJlcl9qb2JzKSkgfD4Kd3JpdGVfY3N2KCJkYXRhL2pvYnNfdG9fY2xlYW4uY3N2IikKCmBgYAoKYGBge3J9CmJvc3NfdG9fY2xlYW4gIDwtIGluZGl2aWR1YWxfZG9ub3JzIHw+IAogIGdyb3VwX2J5KGNvbnRyaWJ1dG9yX2VtcGxveWVyKSB8PgogIHN1bW1hcml6ZSgKICAgIG51bWJlcl9qb2JzID0gbigpLAogICAgbnVtX2RvbmF0aW9ucyA9IG4oKSwgIAogICAgIHRvdGFsX2RvbmF0aW9ucyA9IHN1bSh0b3RhbF9kb25hdGVkKQogICkgfD4gYXJyYW5nZShkZXNjKG51bWJlcl9qb2JzKSkgfD4Kd3JpdGVfY3N2KCJkYXRhL2Jvc3NfdG9fY2xlYW4uY3N2IikKYGBgCgoKYGBge3J9CmNsZWFuX2VtcGxveWVyIDwtIHJlYWRfY3N2KCJkYXRhL2NsZWFuZWRfYm9zcy5jc3YiKQpjbGVhbl9vY2N1cGF0aW9uIDwtIHJlYWRfY3N2KCJkYXRhL2NsZWFuX2pvYnMuY3N2IikKCmNsZWFuX29jY3VwYXRpb24gfD4gCiAgZ3JvdXBfYnkoY2xlYW5lZF9qam9icykgfD4gCiAgc3VtbWFyaXplKAogICAgbnVtYmVyX2pvYnMgPSBuKCksCiAgICBudW1fZG9uYXRpb25zID0gbigpLCAgCiAgICB0b3RhbF9kb25hdGVkID0gc3VtKHRvdGFsX2RvbmF0aW9ucykpIHw+IAogIGFycmFuZ2UoY2xlYW5lZF9qam9icykKCmBgYApgYGB7cn0KCgpjbGVhbl9vY2N1cGF0aW9uIDwtIHJlYWRfY3N2KCJkYXRhL2NsZWFuX2pvYnMuY3N2IikKCgpjbGVhbl9vY2N1cGF0aW9uIDwtIGNsZWFuX29jY3VwYXRpb24gfD4gCiAgbXV0YXRlKGNsZWFuZWRfampvYnMgPSBjYXNlX3doZW4oCiAgICBjb250cmlidXRvcl9vY2N1cGF0aW9uICVpbiUgYygiQy5FLk8uIiwgIkNFTyIsICJDRU8gJiBGT1VOREVSIiwgIkNFTyAmIE1FRElBIENPTlRSSUJVVE9SIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0VPICYgUFJFU0lERU5UIiwgIkNFTyBVTkRFUldSSVRJTkciLCAiQ0VPL0FVVEhPUiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNIQUlSTUFOLCBDRU8gQU5EIFBSRVNJREVOVCIsICJDSElFRiBFWEVDVVRJVkUgT0ZGSUNFUiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNPLUNFTyIsICJDTy1GT1VOREVSICYgQ0VPIiwgIlBSRVNJREVOVCAmIENFTyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZPVU5ERVIgQ0VPIiwgIlBSRVNJREVOVCAmIEMuRS5PLiIsICJQUkVTSURFTlQgLyBDRU8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQUkVTSURFTlQgQ0VPIikgfiAiQ0VPIiwKICAgIGNvbnRyaWJ1dG9yX29jY3VwYXRpb24gJWluJSBjKCJERVBVVFkiLCAiREVQVVRZIEFETUlOSVNUUkFUT1IgJiBESVJFQ1RPUiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRFUFVUWSBBU1NJU1RBTlQgU0VDUkVUQVJZIiwgIkRFUFVUWSBDSElFRiBBRE1JTklTVFJBVElWRSBPRkZJQ0VSIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiREVQVVRZIENPUyIsICJERVBVVFkgRElSRUNUT1IiLCAiREVQVVRZIFJFU0VBUkNIIERJUkVDVE9SIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiREVQVVRZIFNFQ1JFVEFSWSIsICJERVBVVFkgU0VDUkVUQVJZIE9GIENPTU1FUkNFIikgfiAiREVQVVRZIiwKICAgIFRSVUUgfiBjb250cmlidXRvcl9vY2N1cGF0aW9uICAKICApKQoKCm92ZXJhbGxfdG90YWxzIDwtIGNsZWFuX29jY3VwYXRpb24gfD4gCiAgc3VtbWFyaXplKAogICAgdG90YWxfam9icyA9IHN1bShudW1iZXJfam9icywgbmEucm0gPSBUUlVFKSwKICAgIHRvdGFsX2RvbmF0aW9ucyA9IHN1bSh0b3RhbF9kb25hdGlvbnMsIG5hLnJtID0gVFJVRSkKICApCgoKY2VvX2RlcHV0eV9wcm9wb3J0aW9ucyA8LSBjbGVhbl9vY2N1cGF0aW9uIHw+IAogIGZpbHRlcihjbGVhbmVkX2pqb2JzICVpbiUgYygiQ0VPIiwgIkRFUFVUWSIpKSB8PiAKICBncm91cF9ieShjbGVhbmVkX2pqb2JzKSB8PiAKICBzdW1tYXJpemUoCiAgICB0b3RhbF9udW1iZXJfam9icyA9IHN1bShudW1iZXJfam9icywgbmEucm0gPSBUUlVFKSwKICAgIHRvdGFsX251bV9kb25hdGlvbnMgPSBzdW0obnVtX2RvbmF0aW9ucywgbmEucm0gPSBUUlVFKSwKICAgIHRvdGFsX2RvbmF0aW9ucyA9IHN1bSh0b3RhbF9kb25hdGlvbnMsIG5hLnJtID0gVFJVRSkKICApIHw+IAogIG11dGF0ZSgKICAgIHByb3BfbnVtYmVyX2pvYnMgPSByb3VuZCgodG90YWxfbnVtYmVyX2pvYnMgLyBvdmVyYWxsX3RvdGFscyR0b3RhbF9qb2JzKSAqIDEwMCwgMiksCiAgICBwcm9wX3RvdGFsX2RvbmF0aW9ucyA9IHJvdW5kKCh0b3RhbF9kb25hdGlvbnMgLyBvdmVyYWxsX3RvdGFscyR0b3RhbF9kb25hdGlvbnMpICogMTAwLCAyKQogICkKCgpjZW9fZGVwdXR5X3Byb3BvcnRpb25zCgpgYGAKCiNBbnN3ZXIgNDogCgpUbyBhbnN3ZXIgdGhpcyBxdWVzdGlvbiwgd2UgcHV0IGEgY3N2IG9mIG9jY3VwYXRpb25zIGludG8gT3BlblJlZmluZS4gV2Ugb3JpZ2luYWxseSBwbGFubmVkIHRvIGxpbWl0IHRoZSBkYXRhIHNldCwgYnV0IHdlIGZvdW5kIHRoYXQgdGhlcmUgd2VyZSBvbmx5IDEsOTYzIGpvYiB0aXRsZXMgaW4gdGhlIGRhdGEgc2V0LCB3aGljaCBzZWVtZWQgbGlrZSBhIHJlYXNvbmFibGUgbnVtYmVyIHRvIHJlZmluZSBkb3duIGludG8gYSBzbWFsbGVyIGxpc3Qgb2Ygam9icy4gCgpUbyBkbyB0aGlzLCB3ZSBncm91cGVkIGNlcnRhaW4gam9icyBpbnRvIGNhdGVnb3JpZXMgbGlrZSAiZGlyZWN0b3IiIGFuZCAiZXhlY3V0aXZlIiAtLSBzbyBqb2IgdGl0bGVzIGxpa2UgInNhbGVzIGRpcmVjdG9yIiB3ZW50IGludG8gdGhlICJkaXJlY3RvciIgY2F0ZWdvcnkuIAoKV2UgY2hvc2UgdG8gZm9jdXMgb24gQ0VPUyBhbmQgYmVnYW4gYnkgcHVsbGluZyBhbmQgbWVyZ2luZyBhbGwgb2YgdGhlIHNhbWUgdGVybXMgZm9yIENFTy4gVGhlcmUgd2VyZSAyMCBvZiB0aGVtLiBXZSB0aGVuIGRlY2lkZWQgdG8gY29tcGFyZSB0aGF0IHRvIGRlcHV0aWVzLCB3aGljaCB3YXMgZXh0cmVtZWx5IGludGVyZXN0aW5nIHRvIHVzIGJlY2F1c2UgdGhleSBhcmUgZ292ZXJubWVudCBlbXBsb3llZXMuIEFjY29yZGluZyB0byByZXNlYXJjaCwgdGhleSBhcmUgYWxsb3dlZCB0byBkb25hdGUuIFdlIG1lcmdlZCBhbGwgb2YgdGhlIG5hbWVzIHdpdGggZGVwdXR5IHRvZ2V0aGVyLiBUaGVuIHdlIGNyZWF0ZWQgYSBuZXcgZGF0YXNldCB3aXRoIERFUFVUWSBhbmQgQ0VPLCBidXQgdGhlIG51bWJlcnMgYXJlIGRpc3BvcnBvcnRpb25hdGUsIHNvIHdlIG1hZGUgdGhlbSBwcm9wb3J0aW5hdGUgYXMgZm9sbG93czogCnByb3BfbnVtYmVyX2pvYnMJUHJvcG9ydGlvbiBvZiBqb2JzIGluIHRoZSBkYXRhc2V0IGZvciBlYWNoIGNhdGVnb3J5LCByZWxhdGl2ZSB0byB0aGUgdG90YWwgbnVtYmVyIG9mIGpvYnMuCnByb3BfbnVtX2RvbmF0aW9ucwlQcm9wb3J0aW9uIG9mIHRoZSBudW1iZXIgb2YgZG9uYXRpb25zIG1hZGUsIHJlbGF0aXZlIHRvIHRoZSB0b3RhbCBkb25hdGlvbnMgY291bnQuCnByb3BfdG90YWxfZG9uYXRpb25zCVByb3BvcnRpb24gb2YgdGhlIHRvdGFsIGRvbGxhciB2YWx1ZSBkb25hdGVkLCByZWxhdGl2ZSB0byB0aGUgdG90YWwgZG9uYXRpb24gYW1vdW50LgoKT3VyIGFuYWx5c2lzIHNob3dlZCB0aGF0IENFT3MgbWFrZSB1cCBvbmx5IDEuODglIG9mIGFsbCBqb2JzIGluIHRoZSBkYXRhc2V0LCBidXQgdGhleSBhY2NvdW50IGZvciAzLjU5JSBvZiB0aGUgdG90YWwgZG9uYXRpb25zLiBUaGlzIG1lYW5zIHRoYXQgd2hpbGUgdGhlcmUgYXJlbuKAmXQgbWFueSBDRU9zIGNvbXBhcmVkIHRvIG90aGVyIGpvYiB0aXRsZXMsIHRoZXkgZG9uYXRlIGEgbG90IG1vcmUgbW9uZXksIHdoaWNoIHJlZmxlY3RzIHRoZWlyIGZpbmFuY2lhbCBpbmZsdWVuY2UuCgpPbiB0aGUgb3RoZXIgaGFuZCwgREVQVVRZcyxtYW55IG9mIHdob20gYXJlIGdvdmVybm1lbnQgZW1wbG95ZWVzLCBtYWtlIHVwICAwLjA4JSBvZiBhbGwgam9icyBhbmQgY29udHJpYnV0ZSBvbmx5IDAuMDUlIG9mIHRvdGFsIGRvbmF0aW9ucy4gVGhpcyBpcyBhIG11Y2ggc21hbGxlciBwcmVzZW5jZSBjb21wYXJlZCB0byBDRU9zIGFuZCBzaG93cyB0aGF0IHB1YmxpYyBzZWN0b3IgZW1wbG95ZWVzIGxpa2UgREVQVVRZcyBkb25hdGUgZmFyIGxlc3Mgb3ZlcmFsbC4KClRoaXMgaXMgaW1wb3J0YW50IGJlY2F1c2UgaXQgaGlnaGxpZ2h0cyBhIGNsZWFyIGRpZmZlcmVuY2UgYmV0d2VlbiBwcml2YXRlLXNlY3RvciBsZWFkZXJzaGlwIHJvbGVzIGFuZCBnb3Zlcm5tZW50IHBvc2l0aW9ucyB3aGVuIGl0IGNvbWVzIHRvIGRvbmF0aW9ucy4gQ0VPcywgd2hvIG9mdGVuIGVhcm4gaGlnaCBzYWxhcmllcywgc2VlbSB0byBoYXZlIG11Y2ggbW9yZSBmaW5hbmNpYWwgcG93ZXIgdG8gbWFrZSBkb25hdGlvbnMuIERFUFVUWXMsIGluIGNvbnRyYXN0LCBjb250cmlidXRlIGZhciBsZXNzLCB3aGljaCBtaWdodCByZWZsZWN0IGRpZmZlcmVuY2VzIGluIGluY29tZSBvciBydWxlcyBhYm91dCBwb2xpdGljYWwgZ2l2aW5nIGZvciBwdWJsaWMgZW1wbG95ZWVzLgoKV2hhdCBzdGFuZHMgb3V0IGlzIHRoYXQgZG9uYXRpb25zIGFyZW7igJl0IHNwcmVhZCBldmVubHkgYWNyb3NzIGpvYiB0eXBlc+KAlG1vc3Qgb2YgdGhlIG1vbmV5IGNvbWVzIGZyb20gYSBzbWFsbCBncm91cCBvZiBwZW9wbGUgaW4gbGVhZGVyc2hpcCBwb3NpdGlvbnMuIFRoaXMgcmFpc2VzIGludGVyZXN0aW5nIHF1ZXN0aW9ucyBhYm91dCBpbmNvbWUsIGluZmx1ZW5jZSwgYW5kIGhvdyBkb25hdGlvbnMgc2hhcGUgdGhpbmdzIGxpa2UgcG9saXRpY2FsIGNhbXBhaWducyBvciBjaGFyaXRhYmxlIGVmZm9ydHMuCgpUaGlzIGlzIG5ld3N3b3J0aHkgYmVjYXVzZSBpdCBoaWdobGlnaHRzIGhvdyBhIHNtYWxsIGdyb3VwIG9mIGhpZ2gtaW5jb21lIENFT3MgZGlzcHJvcG9ydGlvbmF0ZWx5IGRyaXZlcyBkb25hdGlvbnMsIHJldmVhbGluZyBlY29ub21pYyBkaXNwYXJpdGllcyBhbmQgdGhlIG91dHNpemVkIGZpbmFuY2lhbCBpbmZsdWVuY2Ugb2YgcHJpdmF0ZS1zZWN0b3IgbGVhZGVycy4KCgojUXVlc3Rpb24gNTogV2hhdOKAmXMgdGhlIG1ha2V1cCBvZiBkb25hdGlvbnMgcmVjZWl2ZWQgYnkgSG9nYW4gYW5kIEFsc29icm9va3M/IFdoYXQgcGVyY2VudGFnZSBvZiB0aGVpciBvdmVyYWxsIGRvbmF0aW9ucyB3ZXJlIGxhcmdlIGFtb3VudHMgb2YgbW9uZXkgKHRvIGJlIGRlZmluZWQsIGJ1dCA+ICQxMDAwLCBmb3IgZXhhbXBsZSkgdnMgc21hbGwgYW1vdW50cz8KCmBgYHtyfQpob2dhbl9maWx0ZXJlZF9kYXRhIDwtIGZpbmFsX2RhdGEgfD4KICBmaWx0ZXIoY29tbWl0dGVlX25hbWUgJWluJSBjKCJIT0dBTiBGT1IgTUFSWUxBTkQgSU5DLiIpKQpsYXJnZV9kb25hdGlvbl90aHJlc2hvbGQgPC0gMTAwMAoKaG9nYW5fZmlsdGVyZWRfZGF0YSB8PiBtdXRhdGUoIGRvbmF0aW9uX2NhdGVnb3J5ID0gaWZlbHNlKGNvbnRyaWJ1dGlvbl9yZWNlaXB0X2Ftb3VudCA+IGxhcmdlX2RvbmF0aW9uX3RocmVzaG9sZCwgIkxhcmdlIiwgIlNtYWxsIikgKQoKaG9nYW5fY2F0ZWdvcnlfdG90YWxzIDwtIGhvZ2FuX2ZpbHRlcmVkX2RhdGEgfD4KICBtdXRhdGUoZG9uYXRpb25fY2F0ZWdvcnkgPSBpZmVsc2UoY29udHJpYnV0aW9uX3JlY2VpcHRfYW1vdW50ID4gbGFyZ2VfZG9uYXRpb25fdGhyZXNob2xkLCAiTGFyZ2UiLCAiU21hbGwiKSkgfD4KICBncm91cF9ieShkb25hdGlvbl9jYXRlZ29yeSkgfD4KICBzdW1tYXJpc2UodG90YWxfYW1vdW50ID0gc3VtKGNvbnRyaWJ1dGlvbl9yZWNlaXB0X2Ftb3VudCwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikKCmhvZ2FuX2NhdGVnb3J5X3RvdGFscyAlPiUgbXV0YXRlKCBwZXJjZW50YWdlID0gdG90YWxfYW1vdW50IC8gc3VtKHRvdGFsX2Ftb3VudCkgKiAxMDAgKQoKaG9nYW5fY2F0ZWdvcnlfdG90YWxzCmBgYAoKYGBge3J9CmFsc29icm9va3NfZmlsdGVyZWRfZGF0YSA8LSBmaW5hbF9kYXRhIHw+CiAgZmlsdGVyKGNvbW1pdHRlZV9uYW1lICVpbiUgYygiQUxTT0JST09LUyBGT1IgU0VOQVRFIikpCgpsYXJnZV9kb25hdGlvbl90aHJlc2hvbGQgPC0gMTAwMAoKYWxzb2Jyb29rc19jYXRlZ29yeV90b3RhbHMgPC0gYWxzb2Jyb29rc19maWx0ZXJlZF9kYXRhIHw+CiAgbXV0YXRlKGRvbmF0aW9uX2NhdGVnb3J5ID0gaWZlbHNlKGNvbnRyaWJ1dGlvbl9yZWNlaXB0X2Ftb3VudCA+IGxhcmdlX2RvbmF0aW9uX3RocmVzaG9sZCwgIkxhcmdlIiwgIlNtYWxsIikpIHw+CiAgZ3JvdXBfYnkoZG9uYXRpb25fY2F0ZWdvcnkpIHw+CiAgc3VtbWFyaXNlKHRvdGFsX2Ftb3VudCA9IHN1bShjb250cmlidXRpb25fcmVjZWlwdF9hbW91bnQsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpCgphbHNvYnJvb2tzX2NhdGVnb3J5X3RvdGFscyAgJT4lIG11dGF0ZSggcGVyY2VudGFnZSA9IHRvdGFsX2Ftb3VudCAvIHN1bSh0b3RhbF9hbW91bnQpICogMTAwICkKCmFsc29icm9va3NfY2F0ZWdvcnlfdG90YWxzCmBgYAoKCmBgYHtyfQpob2dhbl9maWx0ZXJlZF9kYXRhIDwtIGhvZ2FuX2ZpbHRlcmVkX2RhdGEgfD4gCiAgbXV0YXRlKGNvbnRyaWJ1dG9yX3ppcCA9IHN1YnN0cihhcy5jaGFyYWN0ZXIoY29udHJpYnV0b3JfemlwKSwgMSwgNSkpCgpgYGAKCmBgYHtyfQpob2dhbl96aXBzIDwtIGhvZ2FuX2ZpbHRlcmVkX2RhdGEgfD4KICBncm91cF9ieShjb250cmlidXRvcl96aXApIHw+CiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQoKd3JpdGVfY3N2KGhvZ2FuX3ppcHMsICJob2dhbl96aXBzLmNzdiIpCmBgYAoKYGBge3J9CmFsc29icm9va3NfZmlsdGVyZWRfZGF0YSA8LSBhbHNvYnJvb2tzX2ZpbHRlcmVkX2RhdGEgfD4gCiAgbXV0YXRlKGNvbnRyaWJ1dG9yX3ppcCA9IHN1YnN0cihhcy5jaGFyYWN0ZXIoY29udHJpYnV0b3JfemlwKSwgMSwgNSkpCgpgYGAKCmBgYHtyfQphbHNvYnJvb2tzX3ppcHMgPC0gYWxzb2Jyb29rc19maWx0ZXJlZF9kYXRhIHw+CiAgZ3JvdXBfYnkoY29udHJpYnV0b3JfemlwKSB8PgogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkKCndyaXRlX2NzdihhbHNvYnJvb2tzX3ppcHMsICJhbHNvYnJvb2tzX3ppcHMuY3N2IikKYGBgCgpgYGB7cn0KaG9nYW5fbGFyZ2VfZGF0YSA8LSBob2dhbl9maWx0ZXJlZF9kYXRhIHw+IAogIG11dGF0ZShjb250cmlidXRvcl96aXAgPSBzdWJzdHIoYXMuY2hhcmFjdGVyKGNvbnRyaWJ1dG9yX3ppcCksIDEsIDUpKSB8PiAKICBmaWx0ZXIoY29udHJpYnV0aW9uX3JlY2VpcHRfYW1vdW50ID4gMTAwMCkgfD4KICBncm91cF9ieShjb250cmlidXRvcl96aXApIHw+CiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQp3cml0ZV9jc3YoaG9nYW5fbGFyZ2VfZGF0YSwgImhvZ2FuX2xhcmdlLmNzdiIpCmBgYAoKYGBge3J9CmFsc29icm9va3NfbGFyZ2VfZGF0YSA8LSBhbHNvYnJvb2tzX2ZpbHRlcmVkX2RhdGEgfD4gCiAgbXV0YXRlKGNvbnRyaWJ1dG9yX3ppcCA9IHN1YnN0cihhcy5jaGFyYWN0ZXIoY29udHJpYnV0b3JfemlwKSwgMSwgNSkpIHw+IAogIGZpbHRlcihjb250cmlidXRpb25fcmVjZWlwdF9hbW91bnQgPiAxMDAwKSB8PgogIGdyb3VwX2J5KGNvbnRyaWJ1dG9yX3ppcCkgfD4KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpCndyaXRlX2NzdihhbHNvYnJvb2tzX2xhcmdlX2RhdGEsICJhbHNvYnJvb2tzX2xhcmdlLmNzdiIpCmBgYAoKQTU6IEhlcmUgd2Ugc2VlIHRoYXQgSG9nYW4gcmVjZWl2ZXMgc2lnbmZpY2FudGx5IG1vcmUgbW9uZXkgaW4gZ2VuZXJhbCBpbiBjb21wYXJpc29uIHRvIEFsc29icm9va3MuIEFkZGl0aW9uYWxseSwgSG9nYW4gcmVjZWl2ZXMgbWFueSBtb3JlIGxhcmdlIGRvbmF0aW9ucyAoaW4gZXhjZXNzIG9mIG1pbGxpb25zKSBpbiBjb21wYXJpc29uIHRvIEFsc29icm9va3MuIFdoZXJlYXMsIHRoZSBzbWFsbCBkb25hdGlvbnMgYXJlIGFyb3VuZCA2MDAsMDAwIG1vcmUuIFRoaXMgc2hvd3MgdGhhdCBlaXRoZXIgSG9nYW4gaGFzIGEgbGFyZ2VyICJmYW4gYmFzZSIgb2YgcGVvcGxlIHdobyBkb25hdGUgdG8gaGltIG9yIHBlcmhhcHMgaGlzIGVsZWN0b3JhdGUgaXMganVzdCB3ZWFsdGhpZXIuIEFkZGl0aW9uYWxseSxpdCB3b3VsZCBiZSBpbnRlcmVzdGluZyB0byBub3RlIHdoZXRoZXIgSG9nYW4gaGFzIGEgaGlnaGVyIGFtb3VudCBvZiBkb25hdGlvbnMgYmVjYXVzZSBoZSB3YXMga25vd24gYmVmb3JlLiAKCldlIHRoZW4gbG9va2VkIGF0IHdoZXJlIHRoZXNlIGRvbmF0aW9ucyB3ZXJlIGZyb20uIEZvciBIb2dhbiwgaGlzIG1vc3QgZG9uYXRpb25zIGNhbWUgZnJvbSBBbm5hcG9saXMgLS0tIGhpcyBmb3JtZXIgcmVzaWRlbmNlIGFuZCB3aGVyZSB0aGUgc3RhdGUgbGVnaXNsYXR1cmUgaXMgbG9jYXRlZC4gSGlzIHNlY29uZCBtb3N0IGRvbmF0aW9ucyBjYW1lIGZyb20gUG90b21hYywgdGhlIHppcCBjb2RlIHdpdGggdGhlIGhpZ2hlc3QgbWVkaWFuIGhvdXNlaG9sZCBpbmNvbWUgaW4gTWFyeWxhbmQuIEhvd2V2ZXIsIDE0IGRpZmZlcmVudCB6aXAgY29kZXMgZG9uYXRlZCBtb3JlIHRvIEFsc29icm9va3MgdGhhbiBBbm5hcG9saXMgZGlkIGZvciBIb2dhbi4gVGhpcyBzaG93ZWQgSG9nYW4gdGhyaXZlZCBvZmYgb2YgbGFyZ2UgZG9uYXRpb25zIGluIGFyZWFzIHRoYXQgQWxzb2Jyb29rcyBkaWQgcmVhY2guCgpXZSB0ZXN0ZWQgdGhpcyB1c2luZyBjaGxvcm9wbGV0aCBtYXBzIHRvIHNob3cgdGhlIHppcCBjb2RlcyB3aGVyZSB0aGUgdHdvIHJlY2VpdmVkIGxhcmdlIGRvbmF0aW9ucy4gVGhlIG1hcCBzaG93ZWQgYSBsb3Qgb2YgYXJlYXMgaW4gbm9ydGhlcm4gTWFyeWxhbmQgaGUgY2FwaXRhbGl6ZWQgd2l0aCB0aG9zZSBkb25hdGlvbnMgd2hlcmUgQWxzb2Jyb29rcyBkaWQgbm90IHJlY2lldmUgYW55LiAgCgpUaGVyZSB3ZXJlIGEgZmV3IHppcCBjb2RlcyB3aGljaCBkaWQgbm90IHJlZ2lzdGVyIG9uIERhdGFXcmFwcGVyIHdoZW4gbWFraW5nIHRoZXNlIGdyYXBocywgYnV0IHRoZXkgZGlkIG5vdCBoYXZlIG1vcmUgdGhhbiBmaXZlIGRvbmF0aW9ucywgc28gd2UgZGlkIG5vdCB0aGluayB0aGF0IGl0IHVsdGltYXRlbHkgYWZmZWN0ZWQgZGV0ZXJtaW5pbmcgd2hlcmUgdGhlIGhvdCBhcmVhcyB3ZXJlIGZvciB0aGUgdHdvIHJlY2VpdmluZyB0aGVpciBkb25hdGlvbnMuIAoKTWFwIG9mIExhcnJ5IEhvZ2FuJ3MgZG9uYXRpb25zIGJ5IHppcGNvZGU6IGh0dHBzOi8vd3d3LmRhdGF3cmFwcGVyLmRlL18vNUxCM1QvCgpNYXAgb2YgQW5nZWxhIEFsc29icm9va3MnIGRvbmF0aW9ucyBieSB6aXBjb2RlOiAKaHR0cHM6Ly93d3cuZGF0YXdyYXBwZXIuZGUvXy9DaHRBci8gCgpNYXAgb2YgQW5nZWxhIEFsc29icm9va3MnIGxhcmdlIGRvbmF0aW9ucyBieSB6aXBjb2RlOgpodHRwczovL3d3dy5kYXRhd3JhcHBlci5kZS9fL0JFdXZZLyAKCk1hcCBvZiBMYXJyeSBIb2dhbidzIGxhcmdlIGRvbmF0aW9ucyBieSB6aXBjb2RlOiAKaHR0cHM6Ly93d3cuZGF0YXdyYXBwZXIuZGUvXy82S1RGMy8=